首页 > 数据库 >如何避免Mysql RR 隔离级别下的 INSERT 死锁

如何避免Mysql RR 隔离级别下的 INSERT 死锁

时间:2024-08-22 15:23:03浏览次数:11  
标签:INSERT RR ConsumeLogAlarmLock 兼容 插入 死锁 key lock id

背景

        我们公司架构师,在使用 Mysql 做分布式锁的时候,因 insert 唯一键冲突,造成死锁。引起我对这部分知识点的兴趣和研究。

        死锁日志的详细信息如下:

LATEST DETECTED DEADLOCK
------------------------
2024-08-19 16:32:45 0x7f92b0ca2700
*** (1) TRANSACTION:
TRANSACTION 329612456, ACTIVE 3 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 3365552, OS thread handle 140263881365248, query id 1432399623 192.168.18.207 root update
-- step6:插入 ConsumeLogAlarmLock
insert into t_distributed_lock (lock_key) values ('ConsumeLogAlarmLock')
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 10113 page no 4 n bits 72 index uk_lock_key of table `shouba_gy`.`t_distributed_lock` trx id 329612456 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) TRANSACTION:
TRANSACTION 329612441, ACTIVE 7 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 1
MySQL thread id 3365470, OS thread handle 140268007991040, query id 1432399406 192.168.18.207 root update
-- step4:插入 ConsumeLogAlarmLock
insert into t_distributed_lock (lock_key) values ('ConsumeLogAlarmLock')
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 10113 page no 4 n bits 72 index uk_lock_key of table `shouba_gy`.`t_distributed_lock` trx id 329612441 lock mode S
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 10113 page no 4 n bits 72 index uk_lock_key of table `shouba_gy`.`t_distributed_lock` trx id 329612441 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** WE ROLL BACK TRANSACTION (2)

前置知识

  • insert 会对插入的这条记录加排他记录锁,在 insert 记录锁之前还会加一种 GAP 锁,叫做插入意向锁,如果出现唯一键冲突,还会加一个共享记录锁(S Next-Key Lock)。具体可以去
    MySQL 官方文档 查看
  • Mysql 5.7 和Mysql 8.0 涉及事务和锁的3 个基表

       Mysql 5.7

                information_schema.innodb_locks

                information_schema.innodb_lock_waits  -- 查看当前等待的锁

                information_schema.innodb_trx -- 查询当前的事务信息

        Mysql 8.0

                performance_schema.data_locks

                performance_schema.data_lock_waits  -- 查看当前等待的锁

                information_schema.INNODB_TRX -- 查询当前的事务信息

        MySQL 5.7 中,information_schema.innodb_locks 包含这些数据:

                InnoDB 事务已申请但未获得的锁。

                InnoDB 事务已持有并且阻塞了其它事务的锁。

        MySQL 8.0 中,performance_schema.data_locks 包含这些数据:

                InnoDB 事务已申请但未获得的锁。

                InnoDB 事务正在持有的锁。

  • mysql 锁冲突矩阵

锁的兼容矩阵

`GapInsert IntentionRecordNext-Key
Gap兼容兼容兼容兼容
Insert Intention冲突兼容兼容冲突
Record兼容兼容冲突冲突
Next-Key兼容兼容冲突冲突

注:横向是已经持有的锁,纵向是正在请求的锁

typeISIXSX
IS兼容兼容兼容不兼容
IX兼容兼容不兼容不兼容
S兼容不兼容兼容不兼容
X不兼容不兼容不兼容不兼容

              

前置准备

Mysql 版本: 5.7.17

隔离级别为:REPEATABLE-READ

表结构:

-- 建表
CREATE TABLE `t_distributed_lock` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `lock_key` varchar(255) NOT NULL COMMENT '锁名称',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_lock_key` (`lock_key`) USING BTREE
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

-- 插入数据
insert into t_distributed_lock2 (lock_key) values ('ConsumeLogAlarmLock');

Sql 语句执行流程:

时刻

Session 1

Session 2

Session 3

T1

begin;

delete from t_distributed_lock where lock_key = 'ConsumeLogAlarmLock';

T2

begin;

insert into t_distributed_lock (lock_key) values ('ConsumeLogAlarmLock');

T3

begin;

insert into t_distributed_lock (lock_key) values ('ConsumeLogAlarmLock');

T4

COMMIT;

DEADLOCK,ROLLBACK;

原因分析

T1 时刻    

session1 插入记录成功,此时对应的索引记录被隐式锁保护,未生成锁结构。

T2 时刻

session2 插入记录检测到插入值和 session1 唯一键冲突。

  • session2 帮助 session1 对 lock_key = ConsumeLogAlarmLock 的记录产生了一个显式的锁结构。

  • session2 自身产生 S 型的 NEXT-KEY LOCK,请求范围为 (-∞,ConsumeLogAlarmLock ],但是其只能获取到 (-∞,ConsumeLogAlarmLock ) 的 GAP LOCK,而被 session1 的 lock_key = ConsumeLogAlarmLock 的记录锁阻塞。

T3 时刻

session3 插入记录检测到插入值和 session1 唯一键冲突。

  • session3 自身产生 S 型的 NEXT-KEY LOCK,请求范围为 (-∞,ConsumeLogAlarmLock ],但是其只能获取到 (-∞,ConsumeLogAlarmLock ) 的 GAP LOCK,而被 session1 的 lock_key = ConsumeLogAlarmLock 的记录锁阻塞。

T4 时刻

  • session1 执行 COMMIT,释放X锁。session2 和 session3 都获得 S Next-Key Lock;
  • session2 和 session3 继续执行插入操作,这个时候 INSERT INTENTION LOCK(插入意向锁)出现了,并且由于插入意向锁会被 gap 锁阻塞,所以 session2 和 session3 互相等待,造成死锁。

至此,形成闭环锁等待,死锁条件达成:session2 和 session3 分别想要在插入的间隙 (-∞,ConsumeLogAlarmLock) 获得插入意向锁,但分别被对方持有的 GAP 锁阻塞。

参考文章

技术分享 | 如何避免 RC 隔离级别下的 INSERT 死锁 - 墨天轮

MySQL :: MySQL 8.0 Reference Manual :: 17.7.3 Locks Set by Different SQL Statements in InnoDB

解决死锁之路 - 常见 SQL 语句的加锁分析 - aneasystone's blog

标签:INSERT,RR,ConsumeLogAlarmLock,兼容,插入,死锁,key,lock,id
From: https://blog.csdn.net/weixin_46203834/article/details/141333626

相关文章

  • facefusion整合包cuda 环境报错解决: onnxruntime::ProviderLibrary::Get [ONNXRuntim
    在b站下载了一个up提供的facefusion整合包,运行go-web.bat报错报错信息如下:2024-08-1910:53:07.6316097[E:onnxruntime:Default,provider_bridge_ort.cc:1992onnxruntime::TryGetProviderInfo_CUDA]D:\a\_work\1\s\onnxruntime\core\session\provider_bridge_ort.cc:1637......
  • 易基因:泪腺RRBS+RNA-seq揭示Sjögren综合征相关干眼症的潜在基因|项目文章
    大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。Sjögren综合征(Sjögren’ssyndrome,SS)相关干眼症是一种以泪腺(lacrimalglands,LGs)慢性炎症为特征的难治性自身免疫性疾病,表观遗传因子在SS相关干眼症发病机制中起着至关重要的作用。然而,泪腺中的DNA甲基化变化及其在S......
  • 题解:P7020 [NWRRC2017] Boolean Satisfiability
    题目传送门题目大意给定一个由大小写字母(变量),|和~组成的布尔代数式,变量可以任意赋值为True或False。求对于给定的变量,有多少种赋值方案使得给定的代数式值为True。思路一个一个看,首先考虑|,先假设只有|,则当代数式中有一个变量为True时,代数式的值变为True。因为每一......
  • 关于Arrays.asList返回List无法新增和删除?
    关于Arrays.asList返回的List无法新增和删除?这个是在写项目的时候发现的,然后就分析了一下源码,得其内部原理复现代码示例:publicclassArraysAsList{publicstaticvoidmain(String[]args){Integer[]array={1,2,3,4,5};List<Integer>list=......
  • 安装MySQL报错ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost:3
    今天要在本地装个mysql,发现mysql-5.7.19-winx64版总是报错由于找不到MSVCP120.dll,无法继续执行代码。重新安装程序可能会解决此问题。,去微软官网找到了最新的VisualC++RedistributableforVisualStudio,下载后发现还是不停地报错。怀疑是系统不兼容,没办法只好安装mysql-8.0.37......
  • 海光 FTPM 运行报错:Fapi_Provision_Finish() ErrorCode (0x00060025) No EK certifica
    使用的是海光CPU提供的基于固件的FTPM,错误原因是海光没有给TPM提供相应的EK证书。从而导致Fapi_Provision()接口无法通过证书的校验。关于Fapi_Provision()接口的功能,官网提到是:RetrievetheEKtemplate,nonceandcertificate,verifythattheymatchtheTPM’sEK......
  • Spyglass cdc check报的errors
    1.report clocksignalsconvergingonamuxslave_adc是在mclk下进行同步,adc_bclk_i则是来自外部,因此切换bclk可能导致毛刺。可以通过切换之前先关闭后级的相关模块。 2.flagsaclocksinalwhosemulti-fanoutsconverge不太清楚要不要解决3.Ac_unsync01(3):Check......
  • mybatis-plus配置自定义sqlInjector(使用InsertBatchSomeColumn),出现Invalid bound stat
    项目一开始未引入mybatis-plus,使用的是mybatis,配置文件为xml,有一个配置类中配置了SqlSessionFactory的相关内容。引入mybatis-plus后,想使用InsertBatchSomeColumn遇到Invalidboundstatement(notfound),多处配置发现没有效果并依旧报错,最终在刚才的配置类中的SqlSessionFact......
  • CF1968E Cells Arrangement
    题意给您一个整数\(n\)。你在网格中选择$n个单元格\((x_1,y_1),(x_2,y_2),\dots,(x_n,y_n)\),其中\(1\lex_i\len\)和\(1\ley_i\len\)。让\(\mathcal{H}\)成为任意一对单元格之间不同的曼哈顿距离的集合。你的任务是最大化\(\mathcal{H}\)的大小。注释中给出了集合及......
  • webpack4升级到webpack5后部分页面出现Uncaught (in promise) ReferenceError: Cannot
    循环依赖当两个或多个模块相互引用时,就会发生JavaScript中的循环依赖(也称为循环依赖)。这可以是直接引用(A->B->A):或间接( A->B->C->A):虽然循环依赖可能不会直接导致错误,但它们几乎总是会带来意想不到的后果。这可能表现为TypeScript类型检查缓慢或开发服务器频......