GreatSQL 死锁案例分析
1.背景概述
客户业务发生死锁的报错,根据业务程序日志及业务流程,发现造成死锁的原因是:事务1 delete + insert ,事务2 delete + insert 2个事务交替执行导致的死锁;由于GAP锁阻塞了插入意向锁,并且当delete的数据存在时死锁不会发生,当delete的数据不存在时,会发生死锁。
2.问题复现
本次测试基于 GreatSQL-8.0.32-24,隔离级别为 RR
2.1 创建测试表
greatsql> create database test; greatsql> create table test(c1 int unique key, c2 int, c3 int, c4 int); greatsql> insert into test values (1,1,1,1),(3,3,3,3),(5,5,5,5),(9,9,9,9); greatsql> select * from test; +------+------+------+------+ | c1 | c2 | c3 | c4 | +------+------+------+------+ | 1 | 1 | 1 | 1 | | 3 | 3 | 3 | 3 | | 5 | 5 | 5 | 5 | | 9 | 9 | 9 | 9 | +------+------+------+------+ 4 rows in set (0.01 sec)
2.2 事务执行顺序
按以下事务执行顺序,如果要删除的数据存在,则不会发生死锁;如果要删除的数据不存,并且要删除的数据在同一个GAP锁的区间内则会发生死锁;
时间 | 事务1 | 事务2 |
---|---|---|
T1 | BEGIN; | BEGIN; |
T2 | delete from test where c1=?; | |
T3 | delete from test where c1=?; | |
T4 | insert into test value(?,?,?,?); | |
T5 | insert into test value(?,?,?,?); |
2.3 当delete的数据存在时
事务1:delete
greatsql> begin; Query OK, 0 rows affected (0.00 sec) greatsql> delete from test where c1=3; Query OK, 1 row affected (0.00 sec)
查看锁信息:
greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks; +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ | THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ | 59 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 59 | test | test | c1 | RECORD | X,REC_NOT_GAP | GRANTED | 3, 0x000000000201 | | 59 | test | test | GEN_CLUST_INDEX | RECORD | X,REC_NOT_GAP | GRANTED | 0x000000000201 | +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ 3 rows in set (0.00 sec)
此时事务1给 3, 0x000000000201 这条数据加了 记录锁 X,REC_NOT_GAP
事务2:delete
greatsql> begin; Query OK, 0 rows affected (0.00 sec) greatsql> delete from test where c1=5; Query OK, 1 row affected (0.00 sec)
查看锁信息:
greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks; +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ | THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ | 57 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 57 | test | test | c1 | RECORD | X,REC_NOT_GAP | GRANTED | 5, 0x000000000202 | | 57 | test | test | GEN_CLUST_INDEX | RECORD | X,REC_NOT_GAP | GRANTED | 0x000000000202 | | 59 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 59 | test | test | c1 | RECORD | X,REC_NOT_GAP | GRANTED | 3, 0x000000000201 | | 59 | test | test | GEN_CLUST_INDEX | RECORD | X,REC_NOT_GAP | GRANTED | 0x000000000201 | +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ 6 rows in set (0.00 sec)
此时事务2给 5, 0x000000000202 这条数据加了 记录锁 X,REC_NOT_GAP
事务1:insert
greatsql> insert into test value(3,3,3,3); Query OK, 1 row affected (0.00 sec)
查看锁信息:
greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks; +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ | THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ | 57 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 57 | test | test | c1 | RECORD | X,REC_NOT_GAP | GRANTED | 5, 0x000000000202 | | 57 | test | test | GEN_CLUST_INDEX | RECORD | X,REC_NOT_GAP | GRANTED | 0x000000000202 | | 59 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 59 | test | test | c1 | RECORD | X,REC_NOT_GAP | GRANTED | 3, 0x000000000201 | | 59 | test | test | GEN_CLUST_INDEX | RECORD | X,REC_NOT_GAP | GRANTED | 0x000000000201 | | 59 | test | test | c1 | RECORD | S,GAP | GRANTED | 3, 0x000000000201 | | 59 | test | test | c1 | RECORD | S,GAP | GRANTED | 5, 0x000000000202 | | 59 | test | test | c1 | RECORD | S,GAP | GRANTED | 3, 0x000000000206 | +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ 9 rows in set (0.00 sec)
此时事务1给 被delete删除的数据 3, 0x000000000201 ,插入的数据 3, 0x000000000202,以及相邻的下一条数据 5, 0x000000000202 加了 间隙锁 S,GAP
事务2:insert
greatsql> insert into test value(5,5,5,5); Query OK, 1 row affected (0.01 sec)
查看锁信息:
greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks; +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ | THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ | 57 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 57 | test | test | c1 | RECORD | X,REC_NOT_GAP | GRANTED | 5, 0x000000000202 | | 57 | test | test | GEN_CLUST_INDEX | RECORD | X,REC_NOT_GAP | GRANTED | 0x000000000202 | | 57 | test | test | c1 | RECORD | S,GAP | GRANTED | 5, 0x000000000202 | | 57 | test | test | c1 | RECORD | S,GAP | GRANTED | 9, 0x000000000203 | | 57 | test | test | c1 | RECORD | S,GAP | GRANTED | 5, 0x000000000207 | | 59 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 59 | test | test | c1 | RECORD | X,REC_NOT_GAP | GRANTED | 3, 0x000000000201 | | 59 | test | test | GEN_CLUST_INDEX | RECORD | X,REC_NOT_GAP | GRANTED | 0x000000000201 | | 59 | test | test | c1 | RECORD | S,GAP | GRANTED | 3, 0x000000000201 | | 59 | test | test | c1 | RECORD | S,GAP | GRANTED | 5, 0x000000000202 | | 59 | test | test | c1 | RECORD | S,GAP | GRANTED | 3, 0x000000000206 | +-----------+---------------+-------------+-----------------+-----------+---------------+-------------+-------------------+ 12 rows in set (0.00 sec)
此时事务2给 被delete删除的数据 5, 0x000000000202 ,插入的数据 5, 0x000000000207,以及相邻的下一条数据 9, 0x000000000203 加了 间隙锁 S,GAP
由于GAP锁之间是相互兼容的,所以没有发生锁等待及死锁,此时事务1,事务2都执行完成,可以正常提交。
2.4 当delete的数据不存在时
事务1:delete
greatsql> begin; Query OK, 0 rows affected (0.01 sec) greatsql> delete from test where c1=6; Query OK, 0 rows affected (0.00 sec)
查看锁信息:
greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks; +-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+ | THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+ | 61 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 61 | test | test | c1 | RECORD | X,GAP | GRANTED | 9, 0x000000000203 | +-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+ 2 rows in set (0.00 sec)
此时事务1给 9, 0x000000000203 这条数据加了 间隙锁 X,GAP
事务2:delete
greatsql> begin; Query OK, 0 rows affected (0.00 sec) greatsql> delete from test where c1=7; Query OK, 0 rows affected (0.00 sec)
查看锁信息:
greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks; +-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+ | THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+ | 62 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 62 | test | test | c1 | RECORD | X,GAP | GRANTED | 9, 0x000000000203 | | 61 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 61 | test | test | c1 | RECORD | X,GAP | GRANTED | 9, 0x000000000203 | +-----------+---------------+-------------+------------+-----------+-----------+-------------+-------------------+ 4 rows in set (0.00 sec)
此时事务2给 9, 0x000000000203 这条数据加了 间隙锁 X,GAP 间隙锁可以相互兼容,因此没有报错
事务1:insert
greatsql> insert into test value(6,6,6,6); ---hang住,处于锁等待
查看锁信息:
greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks; +-----------+---------------+-------------+------------+-----------+------------------------+-------------+-------------------+ | THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +-----------+---------------+-------------+------------+-----------+------------------------+-------------+-------------------+ | 62 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 62 | test | test | c1 | RECORD | X,GAP | GRANTED | 9, 0x000000000203 | | 61 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 61 | test | test | c1 | RECORD | X,GAP | GRANTED | 9, 0x000000000203 | | 61 | test | test | c1 | RECORD | X,GAP,INSERT_INTENTION | WAITING | 9, 0x000000000203 | +-----------+---------------+-------------+------------+-----------+------------------------+-------------+-------------------+ 5 rows in set (0.00 sec)
此时事务1,要申请给 9, 0x000000000203 这条数据加 间隙锁,插入意向锁 X,GAP,INSERT_INTENTION;由于事务2已经加了 间隙锁 X,GAP 间隙锁与插入意向锁并不兼容,因此事务1的insert处于锁等待状态
事务2:insert
greatsql> insert into test value(7,7,7,7); ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
查看锁信息:
greatsql> select THREAD_ID,OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA from performance_schema.data_locks; +-----------+---------------+-------------+-----------------+-----------+------------------------+-------------+------------------------+ | THREAD_ID | OBJECT_SCHEMA | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +-----------+---------------+-------------+-----------------+-----------+------------------------+-------------+------------------------+ | 61 | test | test | NULL | TABLE | IX | GRANTED | NULL | | 61 | test | test | c1 | RECORD | X,GAP | GRANTED | 9, 0x000000000203 | | 61 | test | test | c1 | RECORD | X,GAP | GRANTED | 6, 0x000000000213 | | 61 | test | test | GEN_CLUST_INDEX | RECORD | X | GRANTED | supremum pseudo-record | | 61 | test | test | GEN_CLUST_INDEX | RECORD | X,GAP | GRANTED | 0x000000000213 | | 61 | test | test | c1 | RECORD | X,GAP,INSERT_INTENTION | GRANTED | 9, 0x000000000203 | +-----------+---------------+-------------+-----------------+-----------+------------------------+-------------+------------------------+ 6 rows in set (0.01 sec)
事务2回滚,只有事务1的加锁信息。
由于此时事务2,要申请给 9, 0x000000000203 这条数据加 间隙锁,插入意向锁由于事务1已经加了 间隙锁 X,GAP 间隙锁与插入意向锁并不兼容,因此事务2的insert处于锁等待状态,2个事务相互等待锁导致死锁,此时事务2回滚。
3.总结
此次死锁的发生主要是GAP 锁 和 插入意向锁的冲突,建议让业务修改一下逻辑,先判断数据是否存在 select 一下,存在的话 delete 后在 insert ; 不存在的话直接 insert 不用delete了。
Enjoy GreatSQL :)
关于 GreatSQL
GreatSQL是适用于金融级应用的国内自主开源数据库,具备高性能、高可靠、高易用性、高安全等多个核心特性,可以作为MySQL或Percona Server的可选替换,用于线上生产环境,且完全免费并兼容MySQL或Percona Server。
相关链接: GreatSQL社区 Gitee GitHub Bilibili
GreatSQL社区:
社区有奖建议反馈: https://greatsql.cn/thread-54-1-1.html
社区博客有奖征稿详情: https://greatsql.cn/thread-100-1-1.html
(对文章有疑问或者有独到见解都可以去社区官网提出或分享哦~)
技术交流群:
微信&QQ群:
QQ群:533341697
微信群:添加GreatSQL社区助手(微信号:wanlidbc
)好友,待社区助手拉您进群。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
【项目实战经验】DataKit迁移MySQL到openGauss(下)
上一篇我们分享了安装、设置、链接、启动等步骤,本篇我们将继续分享迁移、启动~ 目录 9. 离线迁移 9.1. 迁移插件安装 中断安装,比如 kill 掉java进程(安装失败也要等待300s) 下载安装包准备上传 缺少mysqlclient lib包 mysql如果是二进制安装的话,我这个版本是没有18这个lib包的 安装成功后的截图 主机上有对应的进程 10. 全量迁移 10.1. 选中主机,启动迁移 10.2. 迁移中 10.3. 迁移结束 10.4. 日志所在目录 11. 增量迁移 11.1. PG里面创建第二个库 11.2. 创建在线迁移任务 11.3. 启动 11.4. 在mysql端进行DDL和DML mysql 端进行了5个事务 第6次增量 停止增量 12. 反向迁移 12.1. 在PG端进行增删改 12.2. PG端DDL 9. 离线迁移 9.1. 迁移插件安装 中断安装,比如 kill 掉java进程(安装失败也要等待300s) update tb_migration_host_portal_install set install_status=10; 下...
- 下一篇
OneDiff 1.0发布!生产环境稳定加速SD/SVD模型
自Stable Diffusion模型发布以来, 硅基流动开发的 OneDiff图片/视频推理加速引擎以其 卓越的性能、“一键”加速的易用性、以及 对最新算法和应用框架的快速支持,深受开发者和用户的喜爱。 今天,OneDiff v1.0.0正式发布。 本次版本更新解决了此前版本v0.13中的问题,主要包括以下新特性、改进以及若干Bug修复: OneDiff质量评估 重复利用编译图 改进对Playground v2.5的支持 支持ComfyUI-AnimateDiff-Evolved 支持ComfyUI_IPAdapter_plus 支持Stable Cascade 提高了VAE的性能 为OneDiff企业版提供了量化工具 欢迎体验新版本,期待你的反馈。完整更新列表请查看: https://github.com/siliconflow/onediff/releases/tag/1.0.0 OneDiff的后续版本将专注于DiT/Sora类模型。 SOTA性能 性能方面,无论是Stable Diffusion还是Stable Video Diffusion模型,OneDiff依然保持2~3...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS7安装Docker,走上虚拟化容器引擎之路
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8编译安装MySQL8.0.19
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作