MySQL InnoDB调试死锁

前提

1、在RR隔离级别下。
2、查看间隙锁是否关闭
区间锁(间隙锁,临键锁)是InnoDB特有施加在索引记录区间的锁,MySQL5.6可以手动关闭区间锁,它由innodb_locks_unsafe_for_binlog参数控制:

  • 设置为ON,表示关闭区间锁,此时一致性会被破坏(所以是unsafe)
  • 设置为OFF,表示开启区间锁

show global variables like “innodb_locks%”;

3、show global variables like “autocommit”; 查看事务是否自动提交。

数据准备

InnoDB的行锁都是实现在索引上的,实验可以使用主键,建表时设定为innodb引擎:

create table t (
id int(10) primary key
)engine=innodb;

-- 插入一些实验数据:
start transaction;
insert into t values(1);
insert into t values(3);
insert into t values(10);
commit;

这是实验的初始状态,不同实验开始之初,都默认回到初始状态。

实验一、间隙锁互斥

开启区间锁,RR的隔离级别下,上例会有:
(-infinity, 1)
(1, 3)
(3, 10)
(10, infinity)
这四个区间。

事务A删除某个区间内的一条不存在记录,获取到共享间隙锁,会阻止其他事务B在相应的区间插入数据,因为插入需要获取排他间隙锁。

-- session A:
set session autocommit=0;
start transaction;
delete from t where id=5;

-- session B:
set session autocommit=0;
start transaction;
insert into t values(0);
insert into t values(2);
insert into t values(12);
insert into t values(7);

事务B插入的值:0, 2, 12都不在(3, 10)区间内,能够成功插入,而7在(3, 10)这个区间内,会阻塞。

可以使用:show engine innodb status; 来查看锁的情况。

MySQL InnoDB调试死锁

insert into t values(7); 正在等待共享间隙锁的释放。

如果事务A提交或者回滚,事务B就能够获得相应的锁,以继续执行。

如果事务A一直不提交,事务B会一直等待,直到超时,超时后会显示:
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

实验二、共享排他锁死锁

回到数据的初始状态,这次需要三个并发的session。

-- session A先执行:
set session autocommit=0;
start transaction;
insert into t values(7);
 
-- session B后执行:
set session autocommit=0;
start transaction;
insert into t values(7);
 
-- session C最后执行:
set session autocommit=0;
start transaction;
insert into t values(7);

三个事务都试图往表中插入一条为7的记录:
(1)A先执行,插入成功,并获取id=7的排他锁;
(2)B后执行,需要进行PK校验,故需要先获取id=7的共享锁,阻塞;
(3)C后执行,也需要进行PK校验,也要先获取id=7的共享锁,也阻塞;

如果此时,session A执行:
rollback;
id=7排他锁释放。

则B,C会继续进行主键校验:
(1)B会获取到id=7共享锁,主键未互斥;
(2)C也会获取到id=7共享锁,主键未互斥;

B和C要想插入成功,必须获得id=7的排他锁,但由于双方都已经获取到id=7的共享锁,它们都无法获取到彼此的排他锁,死锁就出现了。

当然,InnoDB有死锁检测机制,B和C中的一个事务会插入成功,另一个事务会自动放弃:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

实验三、并发间隙锁的死锁

共享排他锁,在并发量插入相同记录的情况下会出现,相应的案例比较容易分析。而并发的间隙锁死锁,是比较难定位的。

回到数据的初始状态,这次需要两个并发的session,其SQL执行序列如下:

A:set session autocommit=0;
A:start transaction;
A:delete from t where id=6;
         B:set session autocommit=0;
         B:start transaction;
         B:delete from t where id=7;
A:insert into t values(5);
         B:insert into t values(8);

A执行delete后,会获得(3, 10)的共享间隙锁。
B执行delete后,也会获得(3, 10)的共享间隙锁。
A执行insert后,希望获得(3, 10)的排他间隙锁,于是会阻塞。
B执行insert后,也希望获得(3, 10)的排他间隙锁,于是死锁出现。

仍然使用:
show engine innodb status;
来查看死锁的情况。

MySQL InnoDB调试死锁

另外,检测到死锁后,事务2自动回滚了:WE ROLL BACK TRANSACTION (2)
事务1将会执行成功。

总结

  • 并发事务,间隙锁可能互斥
    (1)A删除不存在的记录,获取共享间隙锁;
    (2)B插入,必须获得排他间隙锁,故互斥;
  • 并发插入相同记录,可能死锁(某一个回滚)
  • 并发插入,可能出现间隙锁死锁(难排查)
  • show engine innodb status; 可以查看InnoDB的锁情况,也可以调试死锁

文章来源:https://www.cnaaa.net,转载请注明出处:https://www.cnaaa.net/archives/9886

(0)
凯影的头像凯影
上一篇 2023年10月23日 下午5:18
下一篇 2023年10月24日 下午5:15

相关推荐

  • Linux在线yum方式安装mysql5.7(适用于mysql8.0)

    Linux下软件常见部署方式有三种:yum安装、rpm安装以及编译安装。由于离线、编译需要先下载多个文件再安装,步骤较多,所以整理了一下在线安装mysql的方法,文中系统为CentOS7.9版本。 1.配置好yum源,包括epel源 使用官方yum仓库,官方下载链接 2. 生成yum源缓存并查看mysql版本 从enable状态来看,默认启用的是最新8.0版…

    2023年1月1日
    1.6K00
  • 1558 -Column count ot mysql.proc 1s wrong.Expected 20,found 16.Created with MySQL50096, now running 50525. Please use mysql_upgrade to fix this error.

    我在MySQL版本5.5上面创建存储过程的时候就出现了上面的错误。甚至使用show procedure status 查看存储过程都会报上面的错误。 注意操作前先备份!!! 错误原因:是由于你曾经升级过MySQL,或用不同的MySQL版本进行备份迁移恢复。升级和迁移完后未使用mysql_upgrade升级数据结构造成的。 mysql.proc:是MySQL的…

    2023年12月21日
    1.4K00
  • JetBrains产品安装破解分享–DataGrip

    第一步: 下载最新的 Datagrip 2022.3.1 版本安装包 我们先从 Datagrip 官网下载 Datagrip 2022.3.1 版本的安装包,下载链接如下: 第二步:卸载老版本 Datagrip 卸载时删除本地数据 第三步: 开始安装 DataGrip 2022.3.1 版本 安装路径可更改 创建桌面快捷方式 安装完成后勾选 Run Data…

    2023年3月15日
    1.5K00
  • mysql内存占用过高的问题

    现象:在mysql运行一天之后,发现内存占用量达到总内存的百分之77,且swap被占用百分之50 问题检查如下: 第一步:开启内存使用的监控服务 第二步: 查看正常实例的使用情况 第三步: 查看内存监控表数据 问题解决:         当事件的内存占用是performance_schema过高的话,可以考虑关闭或者减少该事件使用内存的量,操作如下

    2023年7月26日
    1.4K00
  • Centos7下配置mysql8.0.20

    mysql8.0.20安装完毕后使用默认密码登陆是无法进行操作的,需要修改默认密码才可以。 我们根据提示,使用ALTER USER修改密码,修改完成后,使用flush privileges; 命令刷新。 ALTER USER root@’localhost’ IDENTIFIED BY ‘Zhangsan@123’; 配置远程登录 mysql安装完成后,默认…

    2022年12月7日
    1.3K00

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

在线咨询: QQ交谈

邮件:712342017@qq.com

工作时间:周一至周五,8:30-17:30,节假日休息

关注微信