测试MySQL锁的问题

InnoDB支持三种行锁:

  • Record Lock:单个行记录上面的锁

  • Gap Lock:间隙锁,锁定一个范围,但不会包含记录本身

  • Next-Key Lock:Gap Lock + Record Lock,锁定一个范围,并且锁定记录本身。

    比如一个索引有10,11,13,20,那么该索引可能被Next-Key Locking锁住的区间为:

    (-∞,10], (10,11], (11, 13], (13, 20], (20, +∞]

1 Record Lock

InnoDB对于primary key(如果是多个列,且查询了所有的列,即点对点查询),会把间隙锁降级为Record Lock,比如下面的例子:

表的schema为:

create table t(
a int PRIMARY KEY
); insert into t select 1;
insert into t select 2;
insert into t select 5;

session A和session B的操作顺序如下:


/*session a*/
begin;
select * from t where a = 5 for update; /*session b*/
begin;
/*不会阻塞,因为对于唯一索引,innodb采用的是record lock,即锁住单行*/
insert into t select 4;
commit; /*session a*/
commit;

2 Next-Key Lock

如果是二级索引,因为不具备唯一性,为了防止幻读的产生,InnoDB会锁住相关的范围,比如下面的例子:

表的schema为:

/*测试Next-Key Lock的问题*/
create table z(
a INT,
b INT,
PRIMARY KEY(a),
KEY(b)
); insert into z select 1,1;
insert into z select 3,1;
insert into z select 5,3;
insert into z select 7,6;
insert into z select 10,8;

session A和session B的操作顺序如下:


/*session a*/
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
begin;
/*按照书里面讲的,这个语句会锁住范围(1,3)(3,6),所以我们尝试下*/
select * from z where b = 3 for update; /*session b*/
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
begin;
/*阻塞,因为session a中锁定了范围,所以这里也会被锁住*/
select * from z where a = 5 lock in share mode;
/*下面的两个语句也会被锁住*/
insert into z select 4,2;
insert into z select 6,5; /*但是下面的语句就不会被锁住,因为不在锁定的范围里面*/
insert into z select 8,6;
insert into z select 2,0;
insert into z select 6,7;
commit; /*session a*/
commit;

2 死锁测试

表的schema如下:

create table t(
a int PRIMARY KEY
); insert into t values(1),(2),(4),(5);

测试MySQL版本:8.0.20

  • 先来看最常见的AB-BA锁问题:

    时间线 session A session B
    1 begin;
    select * from t where a = 2 for update;```
    2 begin;
    select * from t where a = 4 for update;
    3 /*session a:尝试更新4*/
    update t set a = a + 100 where a = 4;
    4 /*session b:尝试更新2,直接发生死锁*/
    update t set a = a + 100 where a= 2;

session B的时间点4的时候,mysql直接报错,并对sessionB进行回滚操作:

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
  • 接下来是另外一种较为隐蔽的死锁;

    先看第一种情况

    时间线 session A session B
    1 begin;
    select * from t where a = 4 for update;
    2 /*因为sessionA中锁定了a=4这一行,所以这里会一直阻塞住,但是,不同的是,它成功的获取到了1,2的行锁,正在等待4的锁*/
    begin;
    select * from t where a <= 4 lock in share mode;
    3 /*发生死锁,虽然session b在等待,但是session b已经获取到了部分的gap lock,即[1,3),所以这里直接报死锁错误*/
    insert into t values(3);

    这里的情况稍微有些不同,sessionB虽然被阻塞了,但是它成功的获取到了a=1,a=22的行锁(Record Lock),此时正在等待a=4的行锁,如果在session A的时间点3的时候,我们允许插入成功,那么sessionA提交后,sessionB lock in share mode成功后,是不是应该在回过头来获取3的Record Lock?这样子是不合理的,所以Mysql这里就认定为死锁,直接回退了undo比较小的事务,那么问题来了,Mysql是怎么做到的呢?个人猜测可能是通过gap lock来实现的,只是在performance_schema.data_locks表中查不到而已,进一步的情况需要查看源代码。

    再来看第二种情况:

    时间线 session A session B
    1 begin;
    select * from t where a = 4 for update;
    select * from t where a = 2 for update;
    2 /*因为sessionA中锁定了a=2和a=4这两行,所以这里会一直阻塞住,但是,不同的是,它成功的获取到了1的行锁,正在等待2的锁*/
    begin;
    select * from t where a <= 4 lock in share mode;
    3 /*和第一种情况不同的是,这里直接成功了,并没有认定为死锁*/
    insert into t values(3);

    这里之所以没有问题,是因为session B在时间点2的时候,已经获取到了a=1的Record Lock,此时正在等待a=2的行锁,因为3>2,所以允许session A对a=3加上X锁,假设session A在时间点3后commit了,这个时候session B在时间点2的语句也成功了,查看performance_schema.data_locks可以看到,session B获取到了a=1, a=2, a=3, a=4的行锁。

    这是Mysql实现锁的一个细节之处,需要好好理解下。

测试MySQL锁的问题的更多相关文章

  1. PHP.39-扩展-锁机制解决并发-MySQL锁、PHP文件锁

    锁机制适用于高并发场景:高并发订单.秒杀…… apache压力测试 Mysql锁详解 语法 加锁:LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE ......... ...

  2. MySQL锁系列3 MDL锁

    http://www.cnblogs.com/xpchild/p/3790139.html   MySQL为了保护数据字典元数据,使用了metadata lock,即MDL锁,保证在并发的情况下,结构 ...

  3. Mysql锁机制--并发事务带来的更新丢失问题

    Mysql 系列文章主页 =============== 刚开始学习 Mysql 锁的时候,觉得 Mysql 使用的是行锁,再加上其默认的可重复读的隔离级别,那就应该能够自动解决并发事务更新的问题.可 ...

  4. Mysql锁机制--索引失效导致行锁变表锁

    Mysql 系列文章主页 =============== Tips:在阅读本文前,最好先阅读 这篇(Mysql锁机制--行锁)文章~ 在上篇文章中,我们看到InnoDB默认的行锁可以使得操作不同行时不 ...

  5. MySQL锁机制&&PHP锁机制,应用在哪些场景中呢?

    正文内容 模拟准备--如何模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本 C:\phpStudy ...

  6. MySQL锁之三:MySQL的共享锁与排它锁编码演示

    一.行锁之MySQL  使用SELECT ... FOR UPDATE 做事务写入前的确认 以MySQL 的InnoDB 为例,预设的Tansaction isolation level 为REPEA ...

  7. 对mysql锁机制的学习

    1.对于mysql学习,经常翻看一些博客,论坛,好像或多或少有mysq锁机制的学习与总结,所以今天有必要 对mysql锁机制的一些个人的总结,以便以后深入的学习. 2.学习这件事,从来都是“深入浅出” ...

  8. MySQL锁机制和PHP锁机制

    模拟准备--如何模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本例如:cmd: apache安装路 ...

  9. [MySQL] 锁/死锁问题一例

    MySQL锁/死锁问题 在MySQL中, 不同事务隔离级别下, 锁的情况表现是不同的, 另外表的设计上有无索引也是一个因素. 做一个小的实验测试InnoDB锁表现 -:) 说明 事务隔离级别 READ ...

随机推荐

  1. 曾经我认为C语言就是个弟弟

    本文所有代码,均上传至github,如果你想直接看源代码,请到github下载,下载地址:https://github.com/vitalitylee/TextEditor "C语言只能写有 ...

  2. Sping AOP

    Sping AOP 1.什么是AOP 面向切面编程(AOP) 是 面向对象编程的补充(OOP) 传统的业务处理代码中,通常会惊醒事务处理.日志处理等操作.虽然可以使用OOP的组合或继承来实现代码重用, ...

  3. OO_Unit1_表达式求导

    CSDN链接 一.第一次作业 1.需求分析 简单多项式导函数 带符号整数 支持前导0的带符号整数,符号可省略,如: +02.-16.19260817等. 幂函数 一般形式 由自变量x和指数组成,指数为 ...

  4. golang:Channel协程间通信

    channel是Go语言中的一个核心数据类型,channel是一个数据类型,主要用来解决协程的同步问题以及协程之间数据共享(数据传递)的问题.在并发核心单元通过它就可以发送或者接收数据进行通讯,这在一 ...

  5. Understanding User and Kernel Mode

    https://blog.codinghorror.com/understanding-user-and-kernel-mode/ Continue Discussion92 repliesJan ' ...

  6. vimdiff env.txt export.txt set.txt

    1. 环境变量 简单理解了变量的概念,就很容易理解环境变量了.环境变量的作用域比自定义变量的要大,如 Shell 的环境变量作用于自身和它的子进程.在所有的 UNIX 和类 UNIX 系统中,每个进程 ...

  7. Windows 常用Cmd命令行 (持续更新...)

    查看IP ipconfig 查看WIFI密码 netsh wlan show profiles wifi_name key = clear 系统探针 systeminfo CMD重定向 输出符号> ...

  8. MegaCli是一款管理维护硬件RAID软件,可以通过它来了解当前raid卡的所有信息,包括 raid卡的型号,raid的阵列类型,raid 上各磁盘状态

    MegaCli 监控raid状态 转载weixin_30344131 最后发布于2015-10-16 13:05:00 阅读数    简介 MegaCli是一款管理维护硬件RAID软件,可以通过它来了 ...

  9. liveCD版: CD光盘映像,和liveDVD一样,唯一的区别就是该版本中包含的软件包会少一点,安装系统时使用 U 盘或者CD光盘进行安装。

    https://man.linuxde.net/download/CentOS/ CentOS,英文全称"Community Enterprise Operating System" ...

  10. .NET6系列:Visual Studio 2022 线路图

    系列目录     [已更新最新开发文章,点击查看详细] 在上一篇博客<Visual Studio 2022>中介绍了VS2022的性能改进与重要功能.本文主要介绍在 Visual Stud ...