mysql 行锁,间隙锁,临键锁,锁范围和死锁实际例子实战

文章目录

背景

想了解下RR事务如何防止幻读的,以及一个实际的死锁例子

锁介绍

行锁(Record Lock):

锁直接加在索引记录上面。通常来说就是锁一行

间隙锁(Gap Lock):

锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间。通常来说是锁区间,可以表示为()

临键锁 ( Next-Key Lock )

行锁与间隙锁组合起来用就叫做Next-Key Lock。 可以表示为[]

bash 复制代码
CREATE TABLE `user` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_id` bigint DEFAULT NULL COMMENT '用户id',
  `mobile_num` bigint NOT NULL COMMENT '手机号',
  PRIMARY KEY (`id`),
  UNIQUE KEY `IDX_USER_ID` (`user_id`),
  KEY `IDX_MOBILE_NUM` (`mobile_num`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb3 COMMENT='用户信息表';

默认数据

id user_id number
1 1 3
5 5 5
8 8 8
9 9 9

每次测试后,回到这个状态

测试

下面测试都是在事务RR级别下的测试的,mysql 版本8.0

唯一键记录存在

事务1
bash 复制代码
START TRANSACTION;
select * from 
demo.`user`  where id=5 for update
//先不提交
COMMIT;

查看锁信息

ENGINE ENGINE_LOCK_ID ENGINE_TRANSACTION_ID THREAD_ID EVENT_ID OBJECT_SCHEMA OBJECT_NAME PARTITION_NAME SUBPARTITION_NAME INDEX_NAME OBJECT_INSTANCE_BEGIN LOCK_TYPE LOCK_MODE LOCK_STATUS LOCK_DATA
INNODB 281473638589224:1068:281473567491024 1856 60 19 demo user 281473567491024 TABLE IX GRANTED
INNODB 281473638589224:2:4:5:281473567488112 1856 60 19 demo user PRIMARY 281473567488112 RECORD X,REC_NOT_GAP GRANTED 5
事务2
bash 复制代码
失败
update demo.`user` set user_id =5 where id=5;
成功
insert demo.`user` values(4,4,4)
成功
insert demo.`user` values(6,6,6)
结论

只会锁id=5这一行

唯一键记录不存在

事务1
bash 复制代码
START TRANSACTION;
select * from 
demo.`user`  where id=6 for update
COMMIT;
事务2
bash 复制代码
成功
insert demo.`user` values(4,4,4)
失败
insert demo.`user` values(5,5,5)
失败
insert demo.`user` values(6,6,6)
失败
insert demo.`user` values(7,7,7)
ENGINE ENGINE_LOCK_ID ENGINE_TRANSACTION_ID THREAD_ID EVENT_ID OBJECT_SCHEMA OBJECT_NAME PARTITION_NAME SUBPARTITION_NAME INDEX_NAME OBJECT_INSTANCE_BEGIN LOCK_TYPE LOCK_MODE LOCK_STATUS LOCK_DATA
INNODB 281473638589224:1068:281473567491024 1875 60 33 demo user 281473567491024 TABLE IX GRANTED
INNODB 281473638589224:2:4:6:281473567488112 1875 60 33 demo user PRIMARY 281473567488112 RECORD X,GAP GRANTED 8
结论

事务A锁住的区间是[6,7],从结果来推导一下,因为id=6这条记录不存在,所以在(5,8)的 间隙都要锁住,因为这些间隙都可能会插入ID=6

范围查询

事务1
bash 复制代码
START TRANSACTION;
select * from 
demo.`user`  where id>=5 and id<=8 for update
COMMIT;
ENGINE ENGINE_LOCK_ID ENGINE_TRANSACTION_ID THREAD_ID EVENT_ID OBJECT_SCHEMA OBJECT_NAME PARTITION_NAME SUBPARTITION_NAME INDEX_NAME OBJECT_INSTANCE_BEGIN LOCK_TYPE LOCK_MODE LOCK_STATUS LOCK_DATA
INNODB 281473638589224:1068:281473567491024 1952 60 110 demo user 281473567491024 TABLE IX GRANTED
INNODB 281473638589224:2:4:5:281473567488112 1952 60 110 demo user PRIMARY 281473567488112 RECORD X,REC_NOT_GAP GRANTED 5
INNODB 281473638589224:2:4:6:281473567488456 1952 60 110 demo user PRIMARY 281473567488456 RECORD X GRANTED 8
事务2
bash 复制代码
成功
insert demo.`user` values(4,4,2)
都失败
insert demo.`user` values(5,5,3)
insert demo.`user` values(6,6,4)
insert demo.`user` values(7,7,5)
insert demo.`user` values(8,8,6)
成功
insert demo.`user` values(10,10,7)
结论

比较好理解,会把[5,8]都锁住

普通索引存在

事务1
bash 复制代码
START TRANSACTION;

select * from 
demo.`user`  where mobile_num=5 for update
COMMIT;
ENGINE ENGINE_LOCK_ID ENGINE_TRANSACTION_ID THREAD_ID EVENT_ID OBJECT_SCHEMA OBJECT_NAME PARTITION_NAME SUBPARTITION_NAME INDEX_NAME OBJECT_INSTANCE_BEGIN LOCK_TYPE LOCK_MODE LOCK_STATUS LOCK_DATA
INNODB 281473638589224:1068:281473567491024 1891 60 66 demo user 281473567491024 TABLE IX GRANTED
INNODB 281473638589224:2:6:7:281473567488112 1891 60 66 demo user IDX_MOBILE_NUM 281473567488112 RECORD X GRANTED 5, 5
INNODB 281473638589224:2:4:5:281473567488456 1891 60 66 demo user PRIMARY 281473567488456 RECORD X,REC_NOT_GAP GRANTED 5
INNODB 281473638589224:2:6:6:281473567488800 1891 60 66 demo user IDX_MOBILE_NUM 281473567488800 RECORD X,GAP GRANTED 8, 8
事务2
bash 复制代码
失败
update demo.`user` set user_id =5 where id=5;

都失败
insert demo.`user` values(10,10,3)
insert demo.`user` values(10,10,4)
insert demo.`user` values(10,10,5)
insert demo.`user` values(10,10,6)
insert demo.`user` values(10,10,7)

成功
insert demo.`user` values(10,10,8)
成功
insert demo.`user` values(10,10,2)
总结

where id=5 会把id=5锁,

set user_id =5 从数据来看,user_id从3到8的区间都可能插入5,所以user_id锁住的区间是(3,8)

普通索引不存在

事务A
bash 复制代码
START TRANSACTION;

select * from 
demo.`user`  where mobile_num =6 for update


COMMIT;
ENGINE ENGINE_LOCK_ID ENGINE_TRANSACTION_ID THREAD_ID EVENT_ID OBJECT_SCHEMA OBJECT_NAME PARTITION_NAME SUBPARTITION_NAME INDEX_NAME OBJECT_INSTANCE_BEGIN LOCK_TYPE LOCK_MODE LOCK_STATUS LOCK_DATA
INNODB 281473638589224:1068:281473567491024 1910 60 84 demo user 281473567491024 TABLE IX GRANTED
INNODB 281473638589224:2:6:6:281473567488112 1910 60 84 demo user IDX_MOBILE_NUM 281473567488112 RECORD X,GAP GRANTED 8, 8
事务B
bash 复制代码
成功
insert demo.`user` values(11,11,4)
失败
insert demo.`user` values(12,12,5)
失败
insert demo.`user` values(10,10,6)
失败
insert demo.`user` values(10,10,7)
成功
insert demo.`user` values(10,10,8)
结论

锁住了【5,8) ,因为6不存在,所以6可能在的区间是(5,8),这里为啥实际上把5锁住了,有点奇怪

死锁例子

事务A 事务B
START TRANSACTION;
START TRANSACTION;
select * from demo.user where id=6 for update
select * from demo.user where id=7 for update
insert demo.user values(6,6,6)
insert demo.user values(7,7,7),然后报死锁

从之前的结论可以分析,5,8这个区间是空的,所以执行for update 操作会把这个区间锁住,两个事务都会对这加锁

相关推荐
2501_9160088944 分钟前
全面介绍Fiddler、Wireshark、HttpWatch、SmartSniff和firebug抓包工具功能与使用
android·ios·小程序·https·uni-app·iphone·webview
Coder_Boy_1 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
玉梅小洋1 小时前
Windows 10 Android 构建配置指南
android·windows
helloworldandy1 小时前
使用Pandas进行数据分析:从数据清洗到可视化
jvm·数据库·python
Libraeking3 小时前
视觉篇:Canvas 自定义绘图与高级动画的华丽圆舞曲
android·经验分享·android jetpack
数据知道3 小时前
PostgreSQL 故障排查:如何找出数据库中最耗时的 SQL 语句
数据库·sql·postgresql
qq_12498707533 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
枷锁—sha3 小时前
【SRC】SQL注入WAF 绕过应对策略(二)
网络·数据库·python·sql·安全·网络安全
Coder_Boy_3 小时前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
Gain_chance3 小时前
35-学习笔记尚硅谷数仓搭建-DWS层最近n日汇总表及历史至今汇总表建表语句
数据库·数据仓库·hive·笔记·学习