怎么解决MySQL死锁问题的?

咱们使用 MySQL 大概率上都会遇到死锁问题,这实在是个令人非常头痛的问题。本文将会对死锁进行相应介绍,对常见的死锁案例进行相关分析与探讨,以及如何去尽可能避免死锁给出一些建议。

话不多说,开整!

什么是死锁

死锁是并发系统中常见的问题,同样也会出现在数据库MySQL的并发读写请求场景中。当两个及以上的事务,双方都在等待对方释放已经持有的锁或因为加锁顺序不一致造成循环等待锁资源,就会出现“死锁”。常见的报错信息为 Deadlock found when trying to get lock...

举例来说 A 事务持有 X1 锁 ,申请 X2 锁,B事务持有 X2 锁,申请 X1 锁。A 和 B 事务持有锁并且申请对方持有的锁进入循环等待,就造成了死锁。

c354ce6dbadfe1aa7326f5f9fdeb6666.png

如上图,是右侧的四辆汽车资源请求产生了回路现象,即死循环,导致了死锁。

从死锁的定义来看,MySQL 出现死锁的几个要素为:

  1. 两个或者两个以上事务

  2. 每个事务都已经持有锁并且申请新的锁

  3. 锁资源同时只能被同一个事务持有或者不兼容

  4. 事务之间因为持有锁和申请锁导致彼此循环等待

InnoDB 锁类型

为了分析死锁,我们有必要对 InnoDB 的锁类型有一个了解。1f31f562a2a1af3272291e8430419824.png

MySQL InnoDB 引擎实现了标准的行级别锁:共享锁( S lock ) 和排他锁 ( X lock )

  1. 不同事务可以同时对同一行记录加 S 锁。

  2. 如果一个事务对某一行记录加 X 锁,其他事务就不能加 S 锁或者 X 锁,从而导致锁等待。

如果事务 T1 持有行 r 的 S 锁,那么另一个事务 T2 请求 r 的锁时,会做如下处理:

  1. T2 请求 S 锁立即被允许,结果 T1 T2 都持有 r 行的 S 锁

  2. T2 请求 X 锁不能被立即允许

如果 T1 持有 r 的 X 锁,那么 T2 请求 r 的 X、S 锁都不能被立即允许,T2 必须等待 T1 释放 X 锁才可以,因为 X 锁与任何的锁都不兼容。共享锁和排他锁的兼容性如下所示:87376a5f55b7556260ddc3ec3e84a97c.png

间隙锁( gap lock )

间隙锁锁住一个间隙以防止插入。假设索引列有2, 4, 8 三个值,如果对 4 加锁,那么也会同时对(2,4)和(4,8)这两个间隙加锁。其他事务无法插入索引值在这两个间隙之间的记录。但是,间隙锁有个例外:

  1. 如果索引列是唯一索引,那么只会锁住这条记录(只加行锁),而不会锁住间隙。

  2. 对于联合索引且是唯一索引,如果 where 条件只包括联合索引的一部分,那么依然会加间隙锁。

next-key lock

next-key lock 实际上就是 行锁+这条记录前面的 gap lock 的组合。假设有索引值10,11,13和 20,那么可能的 next-key lock 包括:

(负无穷,10],(10,11],(11,13],(13,20],(20,正无穷)

在 RR 隔离级别下,InnoDB 使用 next-key lock 主要是防止幻读问题产生。

意向锁( Intention lock )

InnoDB 为了支持多粒度的加锁,允许行锁和表锁同时存在。为了支持在不同粒度上的加锁操作,InnoDB 支持了额外的一种锁方式,称之为意向锁( Intention Lock )。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度上进行加锁。意向锁分为两种:

  1. 意向共享锁( IS ):事务有意向对表中的某些行加共享锁

  2. 意向排他锁( IX ):事务有意向对表中的某些行加排他锁

由于 InnoDB 存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫描以外的任何请求。表级意向锁与行级锁的兼容性如下所示:f0808b5bb9549e5308857ca06bc93164.png

插入意向锁( Insert Intention lock )

插入意向锁是在插入一行记录操作之前设置的一种间隙锁,这个锁释放了一种插入方式的信号,即多个事务在相同的索引间隙插入时如果不是插入间隙中相同的位置就不需要互相等待。假设某列有索引值2,6,只要两个事务插入位置不同(如事务 A 插入3,事务 B 插入4),那么就可以同时插入。

锁模式兼容矩阵

横向是已持有锁,纵向是正在请求的锁:2cd037f717aaffd659a8de5eb52cb4fc.png

阅读死锁日志

在进行具体案例分析之前,咱们先了解下如何去读懂死锁日志,尽可能地使用死锁日志里面的信息来帮助我们来解决死锁问题。

后面测试用例的数据库场景如下:MySQL 5.7 事务隔离级别为 RR

表结构和数据如下:b85073e7c711d6448815ca8590b00b94.png

测试用例如下:e57864661df7c5c5b19a31e4650a6ff5.png

通过执行show engine innodb status 可以查看到最近一次死锁的日志。

日志分析如下:

  1.  (1) TRANSACTION: TRANSACTION 2322, ACTIVE 6 sec starting index read

事务号为2322,活跃 6秒,starting index read 表示事务状态为根据索引读取数据。常见的其他状态有:28d33ae1ebdbe8b41c64c444b976ab4a.png

mysql tables in use 1 说明当前的事务使用一个表。

locked 1 表示表上有一个表锁,对于 DML 语句为 LOCK_IX

LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)

LOCK WAIT 表示正在等待锁,2 lock struct(s) 表示 trx->trx_locks 锁链表的长度为2,每个链表节点代表该事务持有的一个锁结构,包括表锁,记录锁以及自增锁等。本用例中 2locks 表示 IX 锁和lock_mode X (Next-key lock)

1 row lock(s) 表示当前事务持有的行记录锁/ gap 锁的个数。

MySQL thread id 37, OS thread handle 140445500716800, query id 1234 127.0.0.1 root updating

MySQL thread id 37 表示执行该事务的线程 ID 为 37 (即 show processlist; 展示的 ID )

delete from student where stuno=5 表示事务1正在执行的 sql,比较难受的事情是 show engine innodb status 是查看不到完整的 sql 的,通常显示当前正在等待锁的 sql。

 (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw.student trx id 2322 lock_mode X waiting

RECORD LOCKS 表示记录锁, 此条内容表示事务 1 正在等待表 student 上的 idx_stuno 的 X 锁,本案例中其实是 Next-Key Lock 。

事务2的 log 和上面分析类似:

  1.  (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2321 lock_mode X

显示事务 2 的 insert into student(stuno,score) values(2,10) 持有了 a=5 的 Lock mode X

| LOCK_gap,不过我们从日志里面看不到事务2执行的 delete from student where stuno=5;

这点也是造成 DBA 仅仅根据日志难以分析死锁的问题的根本原因。

  1.  (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2321 lock_mode X locks gap before rec insert intention waiting

表示事务 2 的 insert 语句正在等待插入意向锁 lock_mode X locks gap before rec insert intention waiting ( LOCK_X + LOCK_REC_gap )

经典案例分析

案例一:事务并发 insert 唯一键冲突

表结构和数据如下所示:4ea2e023ec71fdd33c9b158811f8d40e.png47db6e0c03c57f185dfd4dc6c96216b4.png测试用例如下:a3c09b60cbf55c6f773f2c72fb347770.png日志分析如下:

  1. 事务 T2 insert into t7(id,a) values (26,10) 语句 insert 成功,持有 a=10 的 排他行锁( Xlocks rec but no gap )

  2. 事务 T1 insert into t7(id,a) values (30,10), 因为T2的第一条 insert 已经插入 a=10 的记录,事务 T1 insert a=10 则发生唯一键冲突,需要申请对冲突的唯一索引加上S Next-key Lock( 即 lock mode S waiting ) 这是一个间隙锁会申请锁住(,10],(10,20]之间的 gap 区域。

  3. 事务 T2 insert into t7(id,a) values (40,9)该语句插入的 a=9 的值在事务 T1 申请的 gap 锁4-10之间, 故需事务 T2 的第二条 insert 语句要等待事务 T1 的 S-Next-key Lock 锁释放,在日志中显示 lock_mode X locks gap before rec insert intention waiting 。

案例一:先 update 再 insert 的并发死锁问题

表结构如下,无数据:7d8cfa0f96f5e38bf4c02ad3fb0b841a.png测试用例如下:76aa2ba28adde0921a1deae91760b9f4.png死锁分析:
可以看到两个事务 update 不存在的记录,先后获得间隙锁( gap 锁),gap 锁之间是兼容的所以在update环节不会阻塞。两者都持有 gap 锁,然后去竞争插入意向锁。当存在其他会话持有 gap 锁的时候,当前会话申请不了插入意向锁,导致死锁。

如何尽可能避免死锁

  1. 合理的设计索引,区分度高的列放到组合索引前面,使业务 SQL 尽可能通过索引定位更少的行,减少锁竞争

  2. 调整业务逻辑 SQL 执行顺序, 避免 update/delete 长时间持有锁的 SQL 在事务前面。

  3. 避免大事务,尽量将大事务拆成多个小事务来处理,小事务发生锁冲突的几率也更小。

  4. 固定的顺序访问表和行。比如两个更新数据的事务,事务 A 更新数据的顺序为 1,2;事务 B 更新数据的顺序为 2,1。这样更可能会造成死锁。

  5. 在并发比较高的系统中,不要显式加锁,特别是是在事务里显式加锁。如 select … for update 语句,如果是在事务里(运行了 start transaction 或设置了autocommit 等于0),那么就会锁定所查找到的记录。

  6. 尽量按主键/索引去查找记录,范围查找增加了锁冲突的可能性,也不要利用数据库做一些额外额度计算工作。比如有的程序会用到 “select … where … order by rand();”这样的语句,由于类似这样的语句用不到索引,因此将导致整个表的数据都被锁住。

  7. 优化 SQL 和表设计,减少同时占用太多资源的情况。比如说,减少连接的表,将复杂 SQL 分解为多个简单的 SQL。


好了。今天就说到这了,我还会不断分享自己的所学所想,希望我们一起走在成功的道路上!

ee0c16936ed89374069ab11cf6bdac7a.gif

往期推荐

42d0aba7ca0d5319d8cde1a0b8aae760.png

一文读懂MySQL查询语句的执行过程


5362f1eb4bb876fec53948b3021899e2.png

虾皮二面:什么是零拷贝?如何实现零拷贝?


b130115dadc3504ea817e980dcebcfcc.png

synchronized底层是如何实现的?


3e3ed809dbca0d191230397b71aba5f9.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544207.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

strictmath_Java StrictMath cos()方法与示例

strictmathStrictMath类cos()方法 (StrictMath Class cos() method) cos() method is available in java.lang package. cos()方法在java.lang包中可用。 cos() method is used to return the trigonometric cosine of an angle of the given parameter in the method. Here, c…

Apache cxf JaxRs基本应用

2019独角兽企业重金招聘Python工程师标准>>> 在前一篇中&#xff0c;我们完成了《Apache cxf JaxWs基本应用》 的编写&#xff0c;我们现在实现一个Restful风格的Cxf 。 一、我们首先依旧是基于Maven project配置pom.xml的依赖 [html] view plaincopyprint? <pr…

白嫖1年阿里云,反手就搭一个Java环境

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;早上收到阿里云小姐姐的消息&#xff0c;阿里云有搞事情了&#xff0c;这次是送一年的阿里云 ECS 服务器。有便宜不占王八蛋…

setseed_Java Random setSeed()方法与示例

setseed随机类setSeed()方法 (Random Class setSeed() method) setSeed() method is available in java.util package. setSeed()方法在java.util包中可用。 setSeed() method is used to set the given seed of this Random Number Generator. setSeed()方法用于设置此随机数生…

.Net 自己写个简单的 半 ORM (练手)

ORM 大家都知道&#xff0c; .Net 是EF 还有一些其他的ORM 从JAVA 中移植过来的 有 &#xff0c; 大神自己写的也有 不管ORM 提供什么附加的 乱七八糟的功能 但是 最主要的 还是 关系映射 的事情。 我自己一直在使用ORMDapper 这个很小的ORM 第一次看到这个ORM 是通过一…

synchronized和ReentrantLock的5个区别!

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在 Java 中&#xff0c;常用的锁有两种&#xff1a;synchronized&#xff08;内置锁&#xff09;和 ReentrantLock&a…

Java Random nextInt()方法与示例

随机类nextInt()方法 (Random Class nextInt() method) Syntax: 句法&#xff1a; public int nextInt();public int nextInt(int num);nextInt() method is available in java.util package. nextInt()方法在java.util包中可用。 nextInt() method is used to return the nex…

《小强升职记》读后感和思维导图

语言幽默轻松&#xff0c;寓教于乐&#xff0c;看完之后有挽起袖子大干一场的冲动&#xff0c;但是诚如书中所言&#xff0c;“不做收藏家&#xff0c;要做建筑工”&#xff0c;实践和坚持才能有所收获。第一次画思维导图(′▽〃)Xmind格式文件转载于:https://www.cnblogs.com/…

oppo后端16连问

前言 大家好&#xff0c;我是磊哥。最近有位读者去面试了oppo&#xff0c;给大家整理了面试真题的答案。希望对大家有帮助哈&#xff0c;一起学习&#xff0c;一起进步。聊聊你印象最深刻的项目&#xff0c;或者做了什么优化。你项目提到分布式锁&#xff0c;你们是怎么使用分布…

java enummap_Java EnumMap values()方法与示例

java enummapEnumMap类values()方法 (EnumMap Class values() method) values() method is available in java.util package. values()方法在java.util包中可用。 values() method is used to get all the values in a Collection view of this enum map. values()方法用于获取…

django 1.8 官方文档翻译:2-1-1 模型语法

模型 模型是你的数据的唯一的、权威的信息源。它包含你所储存数据的必要字段和行为。通常&#xff0c;每个模型对应数据库中唯一的一张表。 基础&#xff1a; 每个模型都是django.db.models.Model 的一个Python 子类。模型的每个属性都表示数据库中的一个字段。Django 提供一套…

实战!阿里神器 Seata 实现 TCC 模式解决分布式事务

今天这篇文章介绍一下Seata如何实现TCC事务模式&#xff0c;文章目录如下&#xff1a;目录什么是TCC模式&#xff1f;TCC&#xff08;Try Confirm Cancel&#xff09;方案是一种应用层面侵入业务的两阶段提交。是目前最火的一种柔性事务方案&#xff0c;其核心思想是&#xff1…

Java Dictionary get()方法与示例

字典类的get()方法 (Dictionary Class get() method) get() method is available in java.util package. get()方法在java.util包中可用。 get() method is used to get the value on the specified key element (key_ele) in this dictionary. get()方法用于获取此字典中指定键…

[CareerCup] 8.10 Implement a Hash Table 实现一个哈希表

8.10 Design and implement a hash table which uses chaining (linked lists) to handle collisions. 这道题让我们实现一个简单的哈希表&#xff0c;我们采用了最简单的那种取余映射的方式来实现&#xff0c;我们使用Cell来保存一对对的key和value的映射关系&#xff0c;然后…

Spring Boot 中实现跨域的 5 种方式,你一定要知道!

一、为什么会出现跨域问题出于浏览器的同源策略限制。同源策略&#xff08;Sameoriginpolicy&#xff09;是一种约定&#xff0c;它是浏览器最核心也最基本的安全功能&#xff0c;如果缺少了同源策略&#xff0c;则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略…

stl:queue 源码_C ++ STL中的queue :: empty()和queue :: size()

stl:queue 源码In C STL, Queue is a type of container that follows FIFO (First-in-First-out) elements arrangement i.e. the elements which inserts first will be removed first. In queue, elements are inserted at one end known as "back" and are delet…

术中导航_密码术中的计数器(CTR)模式

术中导航The Counter Mode or CTR is a simple counter based block cipher implementation in cryptography. Each or every time a counter initiated value is encrypted and given as input to XOR with plaintext or original text which results in ciphertext block. Th…

Android社交类APP动态详情代码实现通用模板

&#xfeff;&#xfeff;Android社交类APP动态详情代码实现通用模板 Android平台上一些比较流行的社交类APP比如微信、陌陌等&#xff0c;都有动态详情页&#xff0c;在该页面&#xff0c;用户发表的动态详情&#xff0c;好友可以发起评论、点赞等等。这种设计在微信和陌陌上大…

聊聊并发编程的12种业务场景

前言并发编程是一项非常重要的技术&#xff0c;无论在面试&#xff0c;还是工作中出现的频率非常高。并发编程说白了就是多线程编程&#xff0c;但多线程一定比单线程效率更高&#xff1f;答&#xff1a;不一定&#xff0c;要看具体业务场景。毕竟如果使用了多线程&#xff0c;…

软件工程编码阶段_软件工程的编码阶段

软件工程编码阶段The coding phase in the software engineering paradigm is usually defined after the designing phase. In this phase, the developers or the coders have to implement the software design practically using any computer language(s) so that the sof…