MySQL innoDB 间隙锁产生的死锁问题
背景
线上经常偶发死锁问题,当时处理一张表,也没有联表处理,但是有两个mq入口,并且消息体存在一样的情况,频率还不是很低,这么一个背景,我非常容易怀疑到,两个消息同时近到这一个事务里面导致的,但是是偶发的,又模拟不出来什么场景会导致死锁,只能进行代码分析,问题还原的方式去排查问题。
业务代码简化成下面
begin
update test set yn = 0 where dm_code = "3";
SELECT * from test where dm_code = '3'
INSERT INTO demand_flow_followers (dm_code, erp )
values
('3', 'a')
,
('3', 'b')
,
('3', 'c')
也就是说先update ,select , insert 这么一个顺序
表中存在dm_code ,erp 唯一索引
如果不存在索引 第一行update 会导致行锁升级为表锁,反而不会导致问题出现,但是并发太差
结论
先说结论:
| session1 | session2 |
|---|---|
| 开启事务 | |
| update | |
| 开启事务 | |
| update | |
| insert | |
| insert出现死锁 |
重点: 无论哪个事务insert,两个事务必须都update 完成,只要满足这个条件,两个insert执行的时候就会报死锁
原因:我先按照自己的理解解释下:
innodb的行锁,存在间隙锁,为啥要去有索引,如果没有索引,第一个update 就直接进行了表锁,这样导致另外一个事务无法进入,就只能进行等待了。
有索引的情况下:
两个事务都执行update,都拿到了[当前值,+∞) 的锁(记录锁+间隙锁),(update的时候,无数据命中)
第一个insert时,希望等待另外一个事务释放锁。第二个事务希望第一个事务释放锁,因此出现了死锁问题
相关知识梳理
InnoDB有三种行锁的算法:
1.Record Lock:是加在索引记录上的。
2.Gap Lock(间隙锁):对索引记录间的范围加锁,或者加在最后一个索引记录的前面或者后面
3.Next-Key Lock:前两种锁的结合,锁定一个范围,并且锁定记录本身,主要目的是解决幻读的问题。
间隙锁主要是防止幻象读,用在Repeated-Read(简称RR)隔离级别下。在Read-Commited(简称RC)下,一般没有间隙锁(有外键情况下例外,此处不考虑)。间隙锁还用于statement based replication
间隙锁有些副作用,如果要关闭,一是将会话隔离级别改到RC下,或者开启 innodb_locks_unsafe_for_binlog(默认是OFF)。
间隙锁(无论是S还是X)只会阻塞insert操作。
CREATE TABLE `test` (
`id` bigint(20) NOT NULL,
`k` bigint(20) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_k` (`k`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
INSERT into test values(2,2),(5,5),(10,10)
select @@global.tx_isolation, @@tx_isolation;
RR隔离级别
delete from test where k=5;
session2
insert into test (id,k) values (3,3)
insert into test (id,k) values (4,4)
insert into test (id,k) values (6,6)
insert into test (id,k) values (7,7)
insert into test (id,k) values (8,8)
insert into test (id,k) values (9,9)
上面都报错:ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
这个证明id (3,5)都被间隙锁锁住了
insert into test (id,k) values (1,1)
insert into test (id,k) values (11,11)
delete from test where id in (1,11)
(3,5) 区间之外都可以执行insert,delete操作
可以看到,delete k=5的记录阻塞了k=3、4、5、6、7、8、9记录的插入操作,事实上,除了对于k=5这条记录上record lock之外,innoDB对于delete和update在辅助索引(非主键索引)上的条件时会对扫过的记录上间隙锁,为了防止幻读,会锁住k=5这条记录的前面一条记录(id=2,k=2)到后面一条记录(id=10,k=10)之间的区间,即锁住k在区间(2,10)的范围(如果没有后一条记录,一直锁到正无穷),至于在边界k=2及k=10上,由于索引内是按照主键排序的,不会锁住(id<2,k=2)但是会锁住(id>2,k=2),同理不会锁住(id>10,k=10)但是会锁住(id<10,k=10).
insert into test (id,k) values (1,2) ok
insert into test (id,k) values (11,2) no
insert into test (id,k) values (11,9) no
insert into test (id,k) values (11,10) ok
insert into test (id,k) values (1,10) no
insert into test (id,k) values (11,10) ok
由于索引内是按照主键排序的,不会锁住(id<2,k=2)但是会锁住(id>2,k=2),同理不会锁住(id>10,k=10)但是会锁住(id<10,k=10).
值得注意的是,delete和update在唯一索引(primary key/unique key)上更新存在的记录时只会上行级记录锁(record key),而在唯一索引上更新不存在的记录时同辅助索引一样会上间隙锁;在上例中,delete id=5只会在(id=5,k=5)这条记录上上X锁,而delete id=7却会锁住(id>5&&id<10)这个区间。
线上问题还原
| session1 | session2 |
|---|---|
| begin | |
| begin | |
| update test set k = 20 where id = 20 | |
| update test set k = 20 where id = 20 | |
| INSERT into test values(25,25) | |
| | | INSERT into test values(25,25) |
重点: insert 之前两个回话都执行完update
SQL 错误 [1213] [40001]: Deadlock found when trying to get lock; try restarting transaction
解决办法:
避免更新或者删除不存在的记录,虽然更新存在的记录也会产生间隙锁,但是间隙锁锁住的范围会更小;
更新不存在的记录会锁住意想不到的区间范围,极其容易导致死锁问题
这些仅仅是解决问题的一个小的技巧,不能从根本上解决问题,如果想从根本上解决就从代码级别上加锁,这样避免了这种问题,但是同时并发就小了,根据自己的实际情况进行定夺方案
作者:京东零售 吴法刚
来源:京东云开发者社区 转载请注明来源
MySQL innoDB 间隙锁产生的死锁问题的更多相关文章
- 巧用MySQL InnoDB引擎锁机制解决死锁问题(转)
该文会通过一个实际例子中的死锁问题的解决过程,进一步解释innodb的行锁机制 最近,在项目开发过程中,碰到了数据库死锁问题,在解决问题的过程中,笔者对MySQL InnoDB引擎锁机制的理解逐步加深 ...
- Mysql innodb 间隙锁
前段时间系统老是出现insert死锁,很是纠结.经过排查发现是间隙锁!间隙锁是innodb中行锁的一种, 但是这种锁锁住的却不止一行数据,他锁住的是多行,是一个数据范围.间隙锁的主要作用是为了防止出现 ...
- Mysql innodb 间隙锁 (转)
MySQL InnoDB支持三种行锁定方式: 行锁(Record Lock):锁直接加在索引记录上面. 间隙锁(Gap Lock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记 ...
- Mysql Innodb 间隙锁浅析
间隙锁说明 innodb引擎自动使用间隙锁来避免幻读(原因是因为innodb采用单行锁+间隙锁组合而成的行锁,会锁定一个范围和记录本身的行),参数默认innodb_locaks_unsafe_for_ ...
- Innodb间隙锁,细节讲解(转)
关于innodb间隙锁,网上有很多资料,在此不做赘述,我们讲解一下关于innodb的间隙锁什么情况下会产生的问题. 网上有些资料说innodb的间隙锁是为了防止幻读,这个论点真的是误人子弟.了解inn ...
- Mysql InnoDB行锁实现方式(转)
Mysql InnoDB行锁实现方式 InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的.InnoDB这种行锁实现特点 ...
- Mysql InnoDB行锁实现方式
Mysql InnoDB行锁实现方式 InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的.InnoDB这种行锁实现特点 ...
- innodb 间隙锁
innodb 间隙锁, 参考 MySQLInnoDB锁机制(二) 针对于辅助索引,也称范围索引 间隙锁只会出现在辅助索引上,唯一索引和主键索引是没有间隙锁.间隙锁(无论是S还是X)只会阻塞insert ...
- 从一个死锁看mysql innodb的锁机制
背景及现象 线上生产环境在某些时候经常性的出现数据库操作死锁,导致业务人员无法进行操作.经过DBA的分析,是某一张表的insert操 作和delete操作发生了死锁.简单介绍下数据库的情况(因为涉及到 ...
- MySQL的间隙锁
什么是间隙锁当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁:对于键值在条件范围内但不存在的记录,叫做“间隙(GAP)”,InnoDB也 ...
随机推荐
- React后台管理系统07 首页布局
注释掉App.tsx中的几个路由组件: 将Home.tsx中的代码使用ant Design网站中的布局进行替换 复制的代码如下: import { DesktopOutlined, FileOutli ...
- 基于DSP的设备振动信号的采集技术方案综述
前记 在能源领域,由于很多地方都是无人值守,设备故障检测是一个必须面对的问题.笔者通过几个行业案例了解到,由于很多设备发生故障时候会产生特定频谱的声音,所以该行业对振动监测的需求特别强烈,由于涉及到 ...
- C++面试八股文:std::deque用过吗?
某日二师兄参加XXX科技公司的C++工程师开发岗位第26面: 面试官:deque用过吗? 二师兄:说实话,很少用,基本没用过. 面试官:为什么? 二师兄:因为使用它的场景很少,大部分需要性能.且需要自 ...
- Linux系统运维之zabbix配置tomcat监控
一.介绍 半年前安装的zabbix监控,当时配合异地的测试人员给A项目做压力测试,主要监控项目部署的几台服务器的内存.CPU信息,以及后来网络I/O等,也没考虑JVM:最近闲下来,想完善下监控,故留此 ...
- G1垃圾回收参数调优及MySQL虚引用造成GC时间过长分析
1. 背景 我方有一应用,偶尔会出现GC时间过长(间隔约4小时),导致性能波动的问题(接口最长需要耗时3秒以上).经排查为G1垃圾回收器参数配置不当 叠加 MySQL 链接超过闲置时间回收,产生大量的 ...
- spingmvc配置AOP 之 非注解方式
spingmvc配置AOP有两种方式,一种是利用注解的方式配置,另一种是XML配置实现. 应用注解的方式配置: 先在maven中引入AOP用到的依赖 <dependency> <gr ...
- 从GaussDB(DWS)的技术演进,看数据仓库的积淀与新生
摘要:随着云计算的兴起和渗透,云数仓成为了数仓技术演进的新阶段,并且逐渐成为了众多企业的共同选择. 本文分享自华为云社区<从GaussDB(DWS)的技术演进,看数据仓库的积淀与新生>,作 ...
- 使用 OpenAPI 构建 RESTful API 文档
作为一名开发者,往往需要编写程序的 API 文档,尤其是 Web 后端开发者,在跟前端对接 HTTP 接口的时候,一个好的 API 文档能够大大提高协作效率,降低沟通成本,本文就来聊聊如何使用 Ope ...
- Hexo博客Next主题文章置顶相关
我需要写一些文章做推荐相关,需要文章置顶功能 博客效果 置顶方法配置 一.修改库文件 原理 在Hexo生成首页HTML时,将top值高的文章排在前面,达到置顶功能. 修改方法 修改Hexo文件夹下的n ...
- 《架构整洁之道》学习笔记 Part 2 编程范式
计算机编程发展至今,一共只有三个编程范式: 结构化编程 面向对象编程 函数式编程 编程范式和软件架构的关系 结构化编程是各个模块的算法实现基础 多态(面向对象编程)是跨越架构边界的手段 函数式编程是规 ...