死锁案例五

mysql死锁分析 环境准备 数据库隔离级别: mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set, 1 warning (0.00 sec) 复制代码 自动提交关闭: mysql> set autocommit=0; Query OK, 0 rows affected ( 阅读详情

来源:公众号yangyidba


一、前言

死锁其实是一个很有意思也很有挑战的技术问题,大概每个 DBA 和部分开发朋友都会在工作过程中遇见。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。本文是源于生产过程中一个死锁案例。

二、背景知识

官方文档[1]中表述:

"REPLACE is done like an INSERT if there is no collision on a unique key. Otherwise, an exclusive next-key lock is placed on the row to be replaced."

"如果没有唯一键冲突的时候,replace 操作和insert的加锁方式是一样的。但是如果有唯一键冲突的话,replace语句执行时,系统会在记录上加上 LOCK X next-key lock。"

如果觉得上面翻译比较简单,就看看下面的介绍[2]

create table t1(
a int auto_increment primary key,
b int,
c int,
unique key (b));
replace into t1(b,c) values (2,3)
Step 1 正常的插入逻辑

首先插入聚集索引,在上例中 a 列为自增列,由于未显式指定,每次 Insert 前都会生成一个不冲突的新值.

随后插入二级索引 b,由于其是唯一索引,在检查 duplicate key 时,加上记录锁,类型为 LOCK_X

对于普通的 INSERT 操作,当需要检查duplicate key 时,加 LOCK_S 锁,而对于 Replace into 或者 INSERT..ON DUPLICATE 操作,则加 LOCK_X 记录锁。当记录已存在,返回错误 DB_DUPLICATE_KEY。

Step 2 处理错误

由于上一步检测到 duplicate key,因此第一步插入的聚集索引记录需要回滚。

Step 3 转换操作

从 InnoDB 层失败返回到 Server 层后,收到 duplicate key 错误,首先检索唯一键冲突的索引,并对冲突的索引记录(及聚集索引记录)加锁

随后确认转换模式以解决冲突:

如果发生 uk 冲突的索引是最后一个唯一索引、没有外键引用、且不存在 delete trigger 时,使用 UPDATE ROW 的方式来解决冲突

否则,使用 DELETE ROW + INSERT ROW 的方式解决冲突, 如果是主键冲突,则会先删除在插入。

Step 4 更新记录

在该例中 a 是主键,对聚集索引和二级索引的更新,都是采用标记删除+插入新记录的方式。对于聚集索引,由于PK列发生变化,采用 delete + insert 聚集索引记录的方式更新。对于二级唯一键索引,同样采用标记删除 + 插入的方式。

三、案例分析

3.1 准备测试环境

事务隔离级别 REPEATABLE READ

数据准备

create table ix(id int not null auto_increment,
a int not null ,
b int not null ,
primary key(id),
idxa(a)
) engine=innodb default charset=utf8;
insert into ix(a,b) valuses(1,1),(5,10),(15,12);
死锁场景

3.2 过程分析

在每次执行一条语句之后都执行 show innodb engine status 查看事务的状态,

执行 replace into ix(a,b) values(5,8)的事务日志如下

---TRANSACTION 1872, ACTIVE 46 sec
4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2
MySQL thread id 1156, OS thread handle 0672, query id 114 localhost msandbox

分析

replace into ix(a,b) values(5,8),因为记录 a=5 已经存在,则会对记录进行更新操作,对记录加 Next Key 锁 RECORD lock,GAP lock,

该事务产生 2 条 undo,持有 4 把锁 一把 IX 锁,1 个 a = 5 的行的行锁,2 个间隙锁 a 在 1-5,5-15 之间的间隙。

执行replace into ix(a,b) values(8,10)的事务日志如下

---TRANSACTION 1873, 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 1155, OS thread handle 3008,
query id 117 localhost msandbox update
replace into ix(a,b) values(8,10)
------- TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 4 n bits 80 
index idx_a of table `test`.`ix` trx id 1873 
lock_mode X locks gap before rec insert intention waiting
---TRANSACTION 1872, ACTIVE 69 sec
4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2

分析

表中没有 a=8 的记录,所以类似 insert into ix(a,b) values(8,10)。但是 a=8 与sess1 持有的 gap lock [5-15] 冲突,于是等待lock_mode X locks gap before rec insert intention waiting,并进入等待队列里面。这把锁是由 sess1 持有。

执行 replace into ix(a,b) values(9,12);事务日志如下执行该语句 sess2 立即报 发生死锁

*** (1) TRANSACTION:
TRANSACTION 1866, ACTIVE 8 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 1155, OS thread handle 3008, query id 101 localhost msandbox update
replace into ix(a,b) values(8,10)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 4 n bits 80 index idx_a of table `test`.`ix` trx id 1866 
lock_mode X locks gap before rec insert intention waiting
*** (2) TRANSACTION:
TRANSACTION 1865, ACTIVE 19 sec inserting
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1136, 5 row lock(s),
undo log entries 3
MySQL thread id 1156, OS thread handle 0672,
query id 102 localhost msandbox update
replace into ix(a,b) values(9,12)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 24 page no 4 n bits 80 index idx_a of table `test`.`ix` trx id 1865 lock_mode X
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 4 n bits 80 
index idx_a of table `test`.`ix` trx id 1865 
lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (1)

日志分析

  1. replace into ix(a,b) values(9,12); 和插入(8,10) 类似需要申请 lock_mode X locks gap before rec insert intention waiting,并且进入申请锁的队列等待。

  2. 事务 T2 replace into ix(a,b) values(5,8); 该语句持有 4 把锁 一把 IX 锁,1 个 a=5 的行的行锁,2 个 a 在 1-5,5-15 之间的 GAP 锁。

  3. 事务 T1 replace into ix(a,b) values(8,10); a=8 与sess1 持有的 gap lock [5,15] 冲突,于是等待 lock_mode X locks gap before rec insert intention waiting,并进入等待队列里面。

  4. 事务 T2 replace into ix(a,b) values(9,12), a=9 也在[5-15]之间,需要等待 T1 的 insert intention lock 释放,T1 等待 T2(SQL1) ,T2(SQL2)等 T1 进而导致死锁 ,系统选择回滚事务 T1。

四、总结

分析定位到问题,怎么解决?目前给开发的建议是避免使用 replace into 方式,使用单条 select 检查 + insert 的方式 或者如果可以接受一定的死锁,可以减少并发执行改为串行。有兴趣的朋友可以自己复现,有更好的解决方法, 可以相互交流。

五、参考

[1] https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html 中阐述了各种语句的加锁方式,对死锁有兴趣的同学一定不要错过。

[2] http://mysqllover.com/?p=1312

本文转自杨奇龙老师的公众号(yangyidba),他长期关注于数据库技术以及性能优化,故障案例分析,数据库运维技术知识分享,个人成长和自我管理等主题,欢迎扫码关注。

扩展阅读

全文完。

Enjoy MySQL :)

知数堂新课程K8S上线了

扫码开启新的学习之旅吧

MySQL死锁案例分:先delete,再insert,导致死锁 session1 已获取到IX锁,gap锁, 等待rec insert intention(插入意向锁), session1, session2 都在等待插入意向锁, 插入意向锁与gap锁冲突,双方都没有释放gap锁,又都在等待插入意向锁,死锁发生。或独占锁,对记录加了排他锁之后,只有拥有该锁的事务可以读取和修改,其他事务都不可以读取和修改,并且同一时间只能有一个事务加写锁。加了读锁的记录,所有的事务都可以读取,但是不能修改,并且可同时有多个事务对记录加读锁。 阅读详情

相关推荐

关于MII、RMII、GMII、RGMII、PHY、网络变压器、RJ45的硬件总结

文章目录前言一、网络传输结构及原理1.以太网的工作原理2.TCP/IP协议3.数据链路层(MAC)二、介质独立接口MII,RMII,GMII,RGMII1.MII(Media Independent interface)2.RMII(Reduced Media Independent Interface)3.GMII(Gigabit Medium Independent)4.RGMII(Reduced Gigabit Media Independent Interface)三、物理层芯片(PHY)二、使用步

weixin_44415816的博客 3万+

Mysql插入删除死锁问题排查

搞懂Mysql插入删除是如何加锁的

qq_42254413的博客 5347

技术面 - 手撕算法题整理

这篇博客整理了华为OD面试中涉及的算法题,包括LeetCode热门题目、OD原题和需要手写的算法,如二分查找、冒泡排序等。建议优先刷"hot100"的LeetCode题目,全刷OD原题,以及掌握手撕算法的基本原理。

m0_73659489的博客 984

用动态的观点看MySQL加锁(问题)

今天这篇答疑文章的主题,即:用动态的观点看加锁。 为了方便你理解,我们再一起复习一下加锁规则。这个规则中,包含了两个“原则”、两个“优化”和一个“bug”: 原则 1:加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭区间。 原则 2:查找过程中访问到的对象才会加锁。 优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。 优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key

ZHY_ERIC的博客 1115

死锁案例

一、前言 死锁其实是一个很有意思也很有挑战的技术问题,大概每个 DBA 和部分开发朋友都会在工作过程中遇见。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。本文是源于生产过程中一个死锁案例。 二、背景知识 官方文档[1]中表述: "REPLACE is done like an INSERT if there is no collision on a unique key. Otherwise, an exclusive next-key lock is placed o

谢谢你,慌乱了我的年华 347

死锁案例十三

一 前言 死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助 二案例分析 2.1 业务场景 用户录入商品,应用程序会提前检查是否存在相同记录,如果有则先删除再插入;如果没有则直接插入。 2.2 环境说明 MySQL 5.7.22 事务隔离级别为RC模式。 create tabl...

weixin_44476888的博客 857

死锁案例

一 前言 死锁其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发朋友都会在工作过程中遇见。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。本文是源于生产过程中一个死锁案例。 二 背景知识 官方文档[1]中表述: “REPLACE is done like an INSERT if there is no collision on a unique k...

weixin_44476888的博客 309

MySQL死锁案例(两条更新导致死锁

测试环境:MySQL 5.7.26 创建测试表: createtablet5(idint); QueryOK,0rowsaffected(0.01sec) 插入测试数据: m...

aa5181的专栏 154

导致mysql 死锁sql_MySQL死锁案例(两条更新导致死锁

测试环境:MySQL 5.7.26创建测试表:createtablet5(idint);QueryOK,0rowsaffected(0.01sec)插入测试数据:mysql>insertintot5values(1);QueryOK,1rowaffected(0.00sec)模拟死锁过程:会话1申请X锁,等待会话2,会话2申请X锁,等待会话1。会话2死锁...

weixin_35330796的博客 416

mysql5.5 增加死锁表_MySQL死锁案例(两条更新导致死锁

测试环境:MySQL 5.7.26创建测试表:createtablet5(idint);QueryOK,0rowsaffected(0.01sec)插入测试数据:mysql>insertintot5values(1);QueryOK,1rowaffected(0.00sec)模拟死锁过程:会话1申请X锁,等待会话2,会话2申请X锁,等待会话1。会话2死锁...

weixin_30119251的博客 235

多线程系列()------ 死锁案例以及检测方法

一、简介 在使用多线程的时候最头疼的问题就是死锁了,不好排查。通过该篇文章,你可以了解常见的死锁案例,引起原因,检测死锁的常用方法以及避免死锁的写法的注意事项。 注:本文主要参考博主wolfcode_cn的文章理解Java死锁死锁检测 以及个人完善。 尊重原创。 二、死锁 2.1 常见引起原因 常见于使用锁嵌套 T...

请叫我猿叔叔的博客 1235

MySQL死锁急救手册】从检测到解决,一篇文章教你彻底摆脱死锁噩梦

当两个或多个事务在执行过程中,因争夺资源而造成的一种相互等待的现象,若无外力干涉,它们都将无法推进下去。fill:#333;color:#333;color:#333;fill:none;是未开启发生死锁自动检测选择牺牲者回滚事务其他事务继续等待超时报错退出预防措施短事务固定顺序降低隔离级别合理索引

全栈开发工程师 1070

MySQL死锁了怎么办(死锁的产生及解决方案),死锁案例死锁的排查,死锁的解决,如何避免死锁的发生

死锁是指2+的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

weixin_44797327的博客 1万+

MySQL死锁死锁产生的4个必要条件,死锁案例, 如何避免死锁

死锁是指2+的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

weixin_44797327的博客 2413

Oracle数据库表的死锁的产生、查询死锁的表信息、死锁的解决

目录 一、死锁产生的原因 二、死锁产生的案例 三、查询死锁的信息 四、死锁的解决方法 1.用户知道死锁的语句的解决办法 2.用户不知道在哪死锁的解决办法 正文 一、死锁产生的原因 其实所有的死锁最深层的原因就是一个:资源竞争。造成这种原因基本上都是不正确的程序设计造成的,经过调整后,基本上都会避免死锁的发生。 二、死锁产生的案例   1:用户1对A表进行Upd...

zxljsbk的博客 2万+

这六个 MySQL 死锁案例,能让你理解死锁的原因

最近总结了一波死锁问题,和大家分享一下! Mysql 锁类型和加锁分析 MySQL有三种锁的级别:页级、表级、行级。 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度 算法: next KeyLocks锁,同时锁住记录(数据),并且锁住记录前面的Gap Gap锁,不锁记录,...

uuuyy_的博客 1221

死锁案例

死锁 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有 "两个以上对象的锁"时,就可能会发生 “死锁” 的问题。 死锁避免方法 产生死锁的四个必要条件: 互斥条件:一个资源每次只能被一个进程使用。 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

AOOOOO李 735

死锁导致的安全事件及预防死锁的实际案例

在各类系统中,死锁现象一旦出现,往往会引发严重的安全事件,对人员生命、财产安全以及业务的正常运转造成巨大威胁。从不同领域的实际案例中,我们能清晰洞察死锁问题的严重性与多样性,以及其背后呈现的发展趋势。

ruanjiananquan99的博客 1150

死锁案例

一 前言死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。二 案...

老叶茶馆 1173

基于Hadoop网站流量日志数据分析系统.zip

基于Hadoop网站流量日志数据分析系统1、典型的离线流数据分析系统 2、技术分析 - Hadoop - nginx - flume - hive - mysql - springboot + mybatisplus+vchartsnginx + lua 日志文件埋点的基于Hadoop网站流量日志数据分析系统1、典型的离线流数据分析系统 2、技术分析 - Hadoop - nginx - flume - hive - mysql - springboot + mybatisplus+vchartsnginx + lua 日志文件埋点的基于Hadoop网站流量日志数据分析系统1、典型的离线流数据分析系统 2、技术分析 - Hadoop - nginx - flume - hive - mysql - springboot + mybatisplus+vchartsnginx + lua 日志文件埋点的

上一篇: MariaDB开发者大会,邀请您免费参加!
下一篇: 组复制安全 | 全方位认识 MySQL 8.0 Group Replication
老叶茶馆_
博客等级 码龄9年 1137粉丝 411原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值