我的编程空间,编程开发者的网络收藏夹
学习永远不晚

数据库死锁分析(行锁、间隙锁)

短信预约 信息系统项目管理师 报名、考试、查分时间动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

数据库死锁分析(行锁、间隙锁)

数据库死锁分析(行锁、间隙锁)

 

分享遇到过的一种间隙锁导致的死锁案例。文后有总结知识供参考

 

日志出现:Deadlock found when trying to get lock; try restarting transaction

导致原因:并发导致的数据库间隙锁死锁(MySql数据库默认RR级别)

 

业务主要操作提炼:首先进来将t1表原来的记录状态更新掉,然后插入新的记录。

线程1

线程2

......

update t1 set status =0 where rule=1

insert  t1 () values (),(),();

update t1 set status =0 where rule=2

insert  t1 () values (),(),();

......

 

 

复现问题:

 

测试数据准备

-- 准备测试表

CREATE TABLE IF NOT EXISTS `test` (

  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT "主键",

  `col` int(11)  NULL COMMENT  "普通字段",

  `idx_col` int(11)  NULL COMMENT "索引字段",

  `uni_col` tinyint(4)  NULL COMMENT "唯一键字段",

  PRIMARY KEY (`id`),

  KEY `idx_1` (`idx_col`) USING BTREE,

  unique KEY `uni_idx_1` (`uni_col`) USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=0 ;

-- 插入测试数据

INSERT INTO `test` ( `col`,`idx_col`,`uni_col`) VALUES

(3,3,3),

(10,10,10),

(11,11,11),

(20,20,20),

(25,25,25),

(26,26,26),

(50,50,50)

;

-- 清空数据表

DELETE FROM test ;

DROP table test;

 

-- 查询测试表数据

SELECT * FROM test;

 

验证间隙锁的存在:

事务1

事务2

-- 开始事务1

BEGIN;

 

 

-- 开始事务2

BEGIN;

-- 更新test,使用索引字段

UPDATE test set col=99 WHERE idx_col=20;

 

 

-- 插入数据,因为有间隙锁,[11-25)这个区间全部被锁上了,插入被阻塞

INSERT INTO `test` ( `idx_col`) VALUES (11);

INSERT INTO `test` ( `idx_col`) VALUES (12);

INSERT INTO `test` ( `idx_col`) VALUES (20);

INSERT INTO `test` ( `idx_col`) VALUES (24);

 

在间隙之外的可以顺利插入

INSERT INTO `test` ( `idx_col`) VALUES (10);
INSERT INTO `test` ( `idx_col`) VALUES (25);
INSERT INTO `test` ( `idx_col`) VALUES (26);

 

由于间隙锁导致的死锁案例:(本次报错复现)

事务1

事务2

-- 开始事务1

BEGIN;

 

 

-- 开始事务2

BEGIN;

-- 更新test,使用索引字段,锁间隙[11,25)

UPDATE test set col=99 WHERE idx_col=20;

 

 

-- 更新test,使用索引字段,锁间隙[20,26)

UPDATE test set col=99 WHERE idx_col=25;

-- 使用了事务2的间隙锁,所以阻塞
INSERT INTO `test` ( `idx_col`) VALUES (21);

 

 

-- 使用了事务1的间隙锁,阻塞,互相需要对方的锁,导致死锁
-- Deadlock found when trying to get lock; try restarting transaction

INSERT INTO `test` ( `idx_col`) VALUES (12);

 

-- 该事务被回滚,事务1提交成功。

 

where条件如果换成唯一键或者主键,没有间隙锁

事务1

事务2

-- 开始事务1

BEGIN;

 

 

-- 开始事务2

BEGIN;

-- 更新test,使用唯一键或主键无间隙锁

UPDATE test set col=99 WHERE uni_col=20;

UPDATE test set col=99 WHERE uni_col=20 AND col=20;

 

 

-- 更新test,使用唯一键或主键无间隙锁

UPDATE test set col=99 WHERE uni_col=25;


UPDATE test set col=99 WHERE uni_col=25 AND col=25;

-- 无间隙锁,顺利执行
INSERT INTO `test` ( `uni_col`) VALUES (21);


INSERT INTO `test` ( `uni_col`,`col`) VALUES (21,21);

 

 

-- 无间隙锁,顺利执行

INSERT INTO `test` ( `uni_col`) VALUES (12);


INSERT INTO `test` ( `uni_col`,`col`) VALUES (12,12);

 

 

如果没有索引,导致全表锁

事务1

事务2

-- 开始事务1

BEGIN;

 

 

-- 开始事务2

BEGIN;

-- 更新test

UPDATE test set col=99 WHERE  col=20;

 

 

-- 以下语句全部阻塞
UPDATE test set col=99 WHERE  col=25;


INSERT INTO `test` ( `uni_col`,`col`) VALUES (100,100);

 

 

 

 

结论:

对于update ,insert组合的这种业务操作,建议update操作使用主键或者唯一键作为where条件可以有效避免并发时候间隙锁的危害。

如果该字段不是主键或者唯一键,建议先查询,使用主键或唯一键进行更新。

或者修改数据库隔离级别为RC级别。

线程1

线程2

......

xx = select id from t1 where rule=1
update t1 set status =0 where id in(xx)

insert  t1 () values (),(),();

xx = select id from t1 where rule=1
update t1 set status =0 where id in(xx)

insert  t1 () values (),(),();

......

 

注意:以下写法无效

update t1 set status=0

WHERE id IN (

select id FROM

(SELECT id FROM t1 where rule=2) t

);

 

  本文来自云海天,作者:wanglifeng,转载请注明原文链接:https://www.cnblogs.com/wanglifeng717/p/15993400.html

 

 

知识示例与参考如下:

 

 

 

 

 

在InnoDB中,主键可以被理解为聚簇索引,聚簇索引中的叶子结点就是相应的数据行,具有聚簇索引的表也被称为聚簇索引表,数据在存储的时候,是按照主键进行排序存储的。

我们都知道,数据库在select的时候,会选择索引列进行查找,索引列都是按照B+树(多叉搜索树)数据结构进行存储,找到主键之后,再回到聚簇索引表中进行查询,这叫回表查询。

 

 

id列是主键,RC或RR隔离级别

只有id=10记录上有行锁

 

 

 

id列是二级唯一索引,RC或RR隔离级别

 

 

 

 

 

id列是二级非唯一索引RC级别

 

 

 

id列是二级非唯一索引RR级别

在RR隔离级别下,为了防止幻读的发生,会使用Gap锁。

这里,你可以把Gap锁理解为,不允许在数据记录前面插入数据。首先,通过id索引定位到第一条满足查询条件的记录,加记录上的X锁,加GAP上的GAP锁,然后加主键聚簇索引上的记录X锁,然后返回;然后读取下一条,重复进行。

直至进行到第一条不满足条件的记录[11,f],此时,不需要加记录X锁,但是仍旧需要加GAP锁,

 

 

 

id上没有索引,RC级别

 

若id列上没有索引,SQL会走聚簇索引的全扫描进行过滤,由于过滤是由MySQL Server层面进行的。因此每条记录,无论是否满足条件,都会被加上X锁。

但是,为了效率考量,MySQL做了优化,对于不满足条件的记录,会在判断后放锁,最终持有的,是满足条件的记录上的锁,但是不满足条件的记录上的加锁/放锁动作不会省略。

同时,优化也违背了2PL的约束(同时加锁同时放锁)

 

 

 

id上没有索引,RR隔离级别

聚簇索引上的所有记录,都被加上了X锁。其次,聚簇索引每条记录间的间隙(GAP),也同时被加上了GAP锁。

MySQL是做了相关的优化的,就是所谓的semi-consistent read。semi-consistent read开启的情况下,对于不满足查询条件的记录,MySQL会提前放锁,同时也不会添加Gap锁。

 

实例:
delete from t1 where pubtime>1 and pubtime<20 and userid="hdc" and comment is not null

 

在RR隔离级别下,针对一个复杂的SQL,首先需要提取其where条件。

Index Key确定的范围,需要加上GAP锁;Index Filter过滤条件,视MySQL版本是否支持ICP,若支持ICP(index condition pushdown ,mysql 5.6),则不满足Index Filter的记录,不加X锁,否则需要X锁;

Table Filter过滤条件,无论是否满足,都需要加X锁。

 

 

 

 

 

 

 

 

 本文来自云海天,作者:wanglifeng,转载请注明原文链接:https://www.cnblogs.com/wanglifeng717/p/15993400.html

 

 

 

 

 

 

 

 

原文地址:https://www.cnblogs.com/wanglifeng717/archive/2022/03/11/15993400.html

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

数据库死锁分析(行锁、间隙锁)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

数据库死锁分析(行锁、间隙锁)

分享遇到过的一种间隙锁导致的死锁案例。文后有总结知识供参考 日志出现:Deadlock found when trying to get lock; try restarting transaction导致原因:并发导致的数据库间隙锁死锁(MySql数据库默认
数据库死锁分析(行锁、间隙锁)
2015-10-31

MySQL数据库之Purge死锁问题的示例分析

小编给大家分享一下MySQL数据库之Purge死锁问题的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!Purge死锁场景说明Purge死锁说明表中存在记录(unique key) 10,20,30,40 (且有 自
2023-05-30

死锁之谜:探究数据库死锁的原因和解决方案

数据库死锁是一种常见的数据库问题,它会导致事务无法继续执行,进而影响数据库的性能和可用性。本文将探讨数据库死锁的原因并提供相应的解决方案,帮助您有效避免和解决数据库死锁问题。
死锁之谜:探究数据库死锁的原因和解决方案
2024-02-05

sqlserver数据库死锁怎么解决

SQL Server数据库死锁是指两个或多个事务相互等待对方的资源而无法继续执行的情况。解决SQL Server数据库死锁的方法包括:监控和诊断死锁:使用SQL Server提供的监控工具如SQL Profiler或Extended Eve
sqlserver数据库死锁怎么解决
2024-04-17

当数据“卡住”:数据库死锁的原理与解析

数据库死锁是指两个或多个事务在竞争资源时,导致彼此无法继续执行的情况。为了解决死锁,需要了解死锁发生的原理并采用合理的死锁处理策略。
当数据“卡住”:数据库死锁的原理与解析
2024-02-05

数据库死锁排查及处理方法

1.查询是否锁表 show OPEN TABLES where In_use > 0; 2.查询进程(如果您有SUPER权限,您可以看到所有线程。否则,您只能看到您自己的线程) show processlist 3.杀死进程id(就是上面命令的id列) kil
数据库死锁排查及处理方法
2015-10-31

数据库常见死锁原因及处理

目录 前言什么是死锁死锁产生的四个必要条件 1. 表锁死锁死锁场景解决方案建议 2. 行锁死锁2.1 两个事务分别想拿到对方持有的锁,互相等待,于是产生死锁死锁场景解决方案 2.2 共享锁转换为排他锁死锁场景解决方案
2023-08-16

数据库死锁:深入剖析数据库中的“死循环”及其应对策略

数据库死锁是指两个或两个以上的事务由于循环等待对方的资源而导致无法继续执行的情况。为了解决死锁问题,数据库使用死锁检测和死锁预防机制,努力打破死锁循环。
数据库死锁:深入剖析数据库中的“死循环”及其应对策略
2024-02-05

Oracle数据库出现锁表情况分析

Oracle数据库出现锁表情况分析:-- 查询所有会话的状态、等待类型及当前正在执行的SQL脚本select se.SID, se.SERIAL#, se.Status, se.Event, se.BLOCKING_SESSION, se.BLOCKING_S
Oracle数据库出现锁表情况分析
2014-12-21

数据库死锁:揭秘数据库中的“舞会”难题!

数据库死锁是事务处理系统中经常遇到的问题,它会导致系统性能下降,甚至导致系统崩溃。本文将揭示数据库死锁的本质,并提供一些解决死锁的策略。
数据库死锁:揭秘数据库中的“舞会”难题!
2024-02-05

SqlServer定时备份数据库和定时杀死数据库死锁解决

上周五组长更我说了一句要杀死数据库的死锁进程,因为自己对数据库不是很熟悉,突然组长说了我也就决定一定要倒腾一下,不然自己怎么提高呢?现在不研究,说不定下次还是要研究呢,倒腾出来了就可以在下次用到了,后来组长又补了一句:"还有定是备份数据库的问题要解决",说干就
SqlServer定时备份数据库和定时杀死数据库死锁解决
2020-11-11

数据库死锁处理的方法有哪些

以下是一些处理数据库死锁的方法:1. 避免死锁:尽量设计良好的数据库结构,避免出现死锁的情况。可以使用合适的事务隔离级别,以及良好的并发控制策略。2. 死锁检测和回滚:当检测到死锁时,可以使用死锁检测算法来确定死锁的存在,并回滚其中一个或多
2023-09-20

编程热搜

目录