mysql lock trx id_MySQL中RR模式下死锁一例
原标题:MySQL中RR模式下死锁一例
作者:高鹏(八怪),《深入理解MySQL主从原理32讲》系列的作者
原文出处:https://www.jianshu.com/p/3e57a428d2a2
一、案例模拟二、死锁分析三、关于锁模式的变化四、关于LOCK_ORDINARY[next_key_lock]来历最后
环境:版本5.7.29,RR隔离级别
一、案例模拟CREATETABLE`t8`(
`id`bigint( 20) NOTNULLAUTO_INCREMENT,
`d_id`varchar( 40) NOTNULLDEFAULT'',
`b_id`varchar( 40) NOTNULLDEFAULT'',
`is_dropped`tinyint( 1) NOTNULLDEFAULT'0',
`u_c`varchar( 10) NOTNULLDEFAULT'',
PRIMARY KEY( `id`),
UNIQUEKEY`DealerAndBrokerAndDropped`( `d_id`, `b_id`, `is_dropped`)
) ENGINE= InnoDB;
insertintot8 values( 1, 1, 1, 0, 'a');
insertintot8 values( 2, 2, 2, 0, 'a');
insertintot8 values( 3, 3, 3, 0, 'a');
insertintot8 values( 4, 4, 4, 0, 'a');
insertintot8 values( 5, 5, 5, 0, 'a');
insertintot8 values( 6, 6, 6, 0, 'a');
insertintot8 values( 7, 7, 7, 0, 'a');
insertintot8 values( 8, 8, 8, 0, 'a');
insertintot8 values( 9, 9, 9, 0, 'a');
insertintot8 values( 10, 10, 10, 0, 'a');
insertintot8 values( 11, 11, 11, 0, 'a');
执行语句如下:
S1
S2
begin
select u_c from t8 where d_id='1' and b_id='1' and is_dropped=0 for update;
select u_c from t8 where d_id='1' and b_id='1' and is_dropped=0 for update; 处于堵塞状态
update t8 set u_c='b' where d_id='1' and b_id='1'; —此时触发死锁 S2回滚
发生死锁记录如下:
二、死锁分析
仔细分析我们会发现trx id 5679最后被堵塞需要获取的锁为(lock_mode X waiting),堵塞发生在索引DealerAndBrokerAndDropped 上,也就是这是一个next key lock 且需要获取的模式为LOCK_X,处于等待状态。
而我们来看trx id 5679前面获取的锁是什么呢?显然可以看到为(lock_mode X locks rec but not gap),获取发生在索引DealerAndBrokerAndDropped 上,也就是这是一个key lock且获取模式为LOCK_X。
但是我们需要知道DealerAndBrokerAndDropped明明是一个唯一索引,获取key lock我们很容易理解,但是为什么也会出现获取next key lock呢?这个问题我们先放一下,先来分析一下整个死锁的产生的过程
S1(select操作)
通过唯一性索引定位索引数据获取了唯一索引DealerAndBrokerAndDropped 上的
LOCK_REC_NOT_GAP|LOCK_X,获取成功记录就是 d_id='1' b_id='1' is_dropped=0这条数据。
S1(select操作)
回表获取全部数据,这个时候需要主键上的相应的行锁。LOCK_REC_NOT_GAP|LOCK_X获取成功
S2(select操作)
通过唯一性索引定位索引数据试图获取了唯一索引DealerAndBrokerAndDropped 上的
LOCK_REC_NOT_GAP|LOCK_X,获取失败记录就是 d_id='1' b_id='1' is_dropped=0这条数据,处于等待状态。
S1(update操作)
通过索引DealerAndBrokerAndDropped 查找数据(注意这里已经不是唯一性定位操作了,下面会做分析),这个时候首先需要通过查询条件获取出需要更新的第一条数据,实际上这个时候也是d_id='1' b_id='1' is_dropped=0这条数据,需要获取的锁为LOCK_ORDINARY[next_key_lock]|LOCK_X,这个时候我发现虽然S1之前获取了这条数据的锁,但是锁模式变化了(一致不会重新获取,下面会分析这种行为),因此这里需要重新获取,但是这显然是不行的,因为S2都还处于等待中,因此这里也发生了等待。
因此通过这个过程就出现死锁,S2等S1 S1等S2。
三、关于锁模式的变化
关于这里我们参考函数lock_rec_lock_fast,这里会不进行行锁冲突验证而进行快速加锁,如果锁模式没有变化则也会再这里进行快速加锁(也就是直接跳过),当然如果块中一个row lock 都没有也会在这里进行加锁,这是每个加行锁的操作都必须经历的判断,如果不能快速加锁则进入slow加锁方式,这里看一下下面的这段代码:
if(lock_rec_get_next_on_page( lock)
|| lock->trx != trx
|| lock->type_mode != (mode | LOCK_REC)
|| lock_rec_get_n_bits( lock) <= heap_no) {
status = LOCK_REC_FAIL;
}
这里的lock->trx != trx会判断本次加锁事务和上次加锁事务是否是同一个事务,lock->type_mode != (mode | LOCK_REC)会判断锁模式是否相同。如果不能满足条件则判定为LOCK_REC_FAIL,进入slow加锁方式。
而我们这里S1加锁第一次是LOCK_REC_NOT_GAP|LOCK_X,而第二次是LOCK_ORDINARY[next_key_lock]|LOCK_X,显然变化了,因此进入slow加锁阶段,进行冲突验证,结果嘛也就冲突了。这是本死锁的一个原因。
四、关于LOCK_ORDINARY[next_key_lock]来历
这是本死锁的一个最重要原因,知道了这个原因这个案例就理解了。首先我们先看这个update语句:
updatet8 setu_c= 'b'whered_id= '1'andb_id= '1';
我们发现这个时候唯一索引还少一个条件也就是is_dropped字段,这个时候本次定位查询不会判定为唯一性查询,而是普通的二级索引定位方式,这个时候RR模式出现LOCK_ORDINARY[next_key_lock]就显得很自然了,下面是这个判断过程,代码位于row_search_mvcc中。
(match_mode == ROW_SEL_EXACT
&& dict_index_is_unique( index)
&& dtuple_get_n_fields(search_tuple)
== dict_index_get_n_unique( index)
&& (dict_index_is_clust( index)
|| !dtuple_contains_null(search_tuple)))
稍微解释一下,唯一性查找条件至少包含如下3点:
1. 索引具有唯一性
2. 查询的字段数量和索引唯一性字段数量相同
3. 是主键或者查询条件中不包含NULL值
注意第3点源码说明如下:
/* Note above that a UNIQUE secondary index can contain many
rows with the same key value if one of the columns is the SQL
null. A clustered index under MySQL can never contain null
columns because we demand that all the columns in primary key
are non-null. */
满足上面4点条件才能确认为唯一查找,本查询由于第3条不满足因此,因此判定失败。
不仅如此如果本条数据加锁成功,那么你会看到如下的结果:
---TRANSACTION 25830, ACTIVE 2 sec
4 lockstruct(s), heapsize1160, 3rowlock(s), undologentries 1
MySQL threadid5, OS threadhandle 140737101231872, queryid4115localhost root starting
showengineinnodbstatus
TABLELOCKtable`test`. `t8`trx id25830lockmodeIX
RECORDLOCKS spaceid1050page no4n bits 80indexDealerAndBrokerAndDropped oftable`test`. `t8`trx id25830lock_mode X
Recordlock, heapno2PHYSICALRECORD: n_fields 4; compact format; info bits 0
0: len 1; hex 31; asc 1;;
1: len 1; hex 31; asc 1;;
2: len 1; hex 80; asc ;;
3: len 8; hex 8000000000000001; asc ;;
RECORD LOCKS space id 1050 page no 3 n bits 80 index PRIMARY of table `test`.`t8` trx id 25830 lock_mode X locks rec but not gap
Record lock, heapno2PHYSICALRECORD: n_fields 7; compact format; info bits 0
0: len 8; hex 8000000000000001; asc ;;
1: len 6; hex 0000000064e6; asc d ;;
2: len 7; hex 5f000000430110; asc _ C ;;
3: len 1; hex 31; asc 1;;
4: len 1; hex 31; asc 1;;
5: len 1; hex 80; asc ;;
6: len 1; hex 62; asc b;;
RECORD LOCKS space id 1050 page no 4 n bits 80 index DealerAndBrokerAndDropped of table `test`.`t8` trx id 25830 lock_mode X locks gap before rec
Record lock, heapno11PHYSICALRECORD: n_fields 4; compact format; info bits 0
0: len 2; hex 3130; asc 10;;
1: len 2; hex 3130; asc 10;;
2: len 1; hex 80; asc ;;
3: len 8; hex 800000000000000a; asc ;;
我们发现DealerAndBrokerAndDropped唯一索引的吓一跳记录也加了gap lock,这完全是RR模式非唯一索引的加锁行为。
最后
如果我们将语句
updatet8 setu_c= 'b'whered_id= '1'andb_id= '1';
修改为
updatet8 setu_c= 'b'whered_id= '1'andb_id= '1'andis_dropped= 0;
那么死锁将不会触发了。原因就是第三部分我们说的,这里锁模式完全一致,不会导致加锁操作了。
enjoy MySQL :)返回搜狐,查看更多
责任编辑:
mysql lock trx id_MySQL中RR模式下死锁一例相关推荐
- mysql len hex asc_MySQL中RR模式下死锁一例
原标题:MySQL中RR模式下死锁一例 作者:高鹏(八怪),<深入理解MySQL主从原理32讲>系列的作者 原文出处:https://www.jianshu.com/p/3e57a428d ...
- vmware中NAT模式下,虚拟机与主机能ping通 为什么虚拟机不能上网
vmware中NAT模式下,虚拟机与主机能ping通 为什么虚拟机不能上网? 方案一: 1.把虚拟机的网络连接设置为桥接或NAT都可以的 2.把虚拟机和主机设置为同一网段 主机 网络邻居属性 3.双击 ...
- 帧中继环境中NBMA模式下OSPF的配置
帧中继环境中NBMA模式下OSPF的配置 在帧中继服务器添加端口 R1(config)#int s1/0 R1(config-if)#ip add 172.16.134.1 255.255.255.0 ...
- mysql rr和rc_MySQL的在RC和RR模式下的锁
InnoDB的锁机制: 数据库使用所是为了支持更好的并发,提供数据的完整性和一致性.InnoDB是一个支持锁的存储引擎,锁的类型有:共享锁(S).排它锁(X).意向共享锁(IS).意向排它锁(IX). ...
- rc 和rr 模式下的死锁问题
问题描述:线上项目高峰期突然出现死锁问题!用户操作出现卡顿,后台直接抛出deadlock 查看日志发现是在update表的时候出现DeadlockLoserDataAccessException异常 ...
- mysql 必须安装php_非root模式下安装mysql php小记
假设你的home目录为/home/work mysql-server 安装 1. 下载mysql.tar.gz wget http://dev.mysql.com/get/Downloads/MySQ ...
- vs2010中release模式下调试程序
debug模式调试信息全,但是速度很慢,在数据量比较大的时候非常影响调试效率,release模式速度快,但是没有调试信息.所以在编译的时候很多编译器会提供一种折中的编译方式,在release下提供调试 ...
- 康耐视InSight中电子表格模式下图案匹配工具FindPatterns的使用分享
1. 准备工作: 打开InSight软件,选择电子表格视图. 修改仿真器:以下所有的程序演示都是在仿真上进行的,请预先将仿真器型号改为标准(见:https://blog.csdn.net/qq_346 ...
- png图片在unity中Default模式下透明区域显示白底的问题
最近在项目开发中遇到一个问题,美术给的png图片在unity中查看的时候Default选项下透明区域会显示黑白色,用于spine动画中会显示白色的色块.但是透明区域显示黑色的地方正常. 打印图片每个像 ...
最新文章
- 伪基站识别技巧(一)
- 我的年龄又快被5整除了......
- “苹果光环”褪色后,瑞声靠什么坐稳头把交椅?
- 怎么判断ajax返回是否成功,如何判断jquery的ajax请求已经返回
- flask的第一个hello word 程序
- leetcode1328. 破坏回文串
- 前端学习(1905)vue之电商管理系统电商系统之根据用户id查询对应的信息
- 微信小程序实现轨迹回放
- 基于FFmpeg的音频编码(PCM数据编码成AAC android)
- v540 检测的问题
- hbase windows 单机版安装
- spring使用 hibernate jpa JpaRepository
- 基于51单片机的模拟信号检测系统
- 基于SSM房屋租赁管理系统
- 丁火生于未月命理分析_丁火生于未月的性格特征
- STM8L051的硬件I2C调试
- 《设计模式》之命令模式
- 2345浏览器怎么换主页 2345浏览器换主页教程
- 考研数学之线性代数知识点
- 雅礼集训 Day1 T1 养花
热门文章
- 基于JAVA+SpringMVC+Mybatis+MYSQL的集市预约管理系统
- 基于JAVA+SpringMVC+Mybatis+MYSQL的教材管理系统
- 【转载】手动删除引用nuget如何还原
- 章节三、2-方法_演示实例
- java怎样获取变量的类型
- NodeJS-001-Nodejs学习文档整理(转-出自http://www.cnblogs.com/xucheng)
- Linux 下 Shell 命令的分类及用法
- linux添加用户、权限
- USACO3.1.1最短网络
- dijkstra java pre_Dijkstra算法实现