MySQL死锁分析

什么是死锁

MySQL的死锁指的是两个事务互相等待的场景,这种循环等待理论上不会有尽头。
比如事务A持有行1的锁,事务B持有行2的锁,
然后事务A试图获取行2的锁,事务B试图获取行1的锁,
这样事务A要等待事务B释放行2的锁,事务B要等待事务A释放行1的锁,
两个事务互相等待,谁也提交不了。这种情况下MySQL会选择中断并回滚其中一个事务,使得另一个事务可以提交。MySQL会记录死锁的日志。

死锁案列

MySQL版本:Server version: 8.0.27 MySQL Community Server - GPL

测试表

    CREATE TABLE `record` (
      `id` bigint(20)  NOT NULL AUTO_INCREMENT,
      `biz_id` bigint(20) NOT NULL DEFAULT 1,
      `vid` bigint(20) NOT NULL DEFAULT '0',
      PRIMARY KEY (`id`),
      index idx_biz_id(`biz_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

初始化数据

insert into record(biz_id,vid) values(10,1231);
insert into record(biz_id,vid) values(12,8716);
insert into record(biz_id,vid) values(14,2948);
insert into record(biz_id,vid) values(16,7463);

+----+--------+------+
| id | biz_id | vid  |
+----+--------+------+
|  1 |     10 | 1231 |
|  2 |     12 | 8716 |
|  3 |     14 | 2948 |
|  4 |     16 | 7463 |
+----+--------+------+

先删再插以此实现insertOrUpdate

步骤 事务A 事务B
1 set autocommit=0;
2 delete from record where id=18;
3 set autocommit=0;
4 delete from record where id=18;
5 insert into record(biz_id,vid) values(18,1231) blocked
6 insert into record(biz_id,vid) values(18,1231) ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
7 Query OK, 1 row affected (9.30 sec)

查看日志

show engine innodb status

发现死锁日志

LATEST DETECTED DEADLOCK
------------------------
2022-03-03 16:41:21 0x70000da29000
*** (1) TRANSACTION:
// 事务ID,活跃时间,事务正在进行的操作
TRANSACTION 1897, ACTIVE 79 sec inserting
//修改1个表,锁1行数据
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s)
//线程ID和queryId
MySQL thread id 24, OS thread handle 123145545482240, query id 166 localhost root update
// 执行的SQL
insert into record(biz_id,vid) values(18,1231)

*** (1) HOLDS THE LOCK(S):
//持有的锁信息
RECORD LOCKS space id 6 page no 4 n bits 72 index PRIMARY of table `test`.`record` trx id 1897 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;


*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
//等待的锁
RECORD LOCKS space id 6 page no 4 n bits 72 index PRIMARY of table `test`.`record` trx id 1897 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 1898, ACTIVE 49 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s)
MySQL thread id 20, OS thread handle 123145546547200, query id 167 localhost root update
insert into record(biz_id,vid) values(18,1231)

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 6 page no 4 n bits 72 index PRIMARY of table `test`.`record` trx id 1898 lock_mode X
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 6 page no 4 n bits 72 index PRIMARY of table `test`.`record` trx id 1898 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)

MySQL的8种锁

1 行锁(Record Locks)

行锁是作用在索引上的。

2 间隙锁(Gap Locks)

间隙锁是锁住一个区间的锁。
这个区间是一个开区间,范围是从某个存在的值向左直到比他小的第一个存在的值,所以间隙锁包含的内容就是在查询范围内,而又不存在的数据区间。
比如有id分别是1,10,20,要修改id<15的数据,那么生成的间隙锁有以下这些:(-∞,1),(1,10),(10,20),此时若有其他事务想要插入id=11的数据,则需要等待。
间隙锁是不互斥的。
作用是防止其他事务在区间内添加记录,而本事务可以在区间内添加记录,从而防止幻读。
在可重复读这种隔离级别下会启用间隙锁,而在读未提交和读已提交两种隔离级别下,即使使用select ... in share mode或select ... for update,也不会有间隙锁,无法防止幻读。

3 临键锁(Next-key Locks)

临键锁=间隙锁+行锁,于是临键锁的区域是一个左开右闭的区间。
隔离级别是可重复读时,select ... in share mode或select ... for update会使用临键锁,防止幻读。普通select语句是快照读,不能防止幻读。

4 共享锁/排他锁(Shared and Exclusive Locks)

共享锁和排它锁都是行锁。共享锁用于事务并发读取,比如select ... in share mode。排它锁用于事务并发更新或删除。比如select ... for update

5 意向共享锁/意向排他锁(Intention Shared and Exclusive Locks)

意向共享锁和意向排他锁都是表级锁。
官方文档中说,事务获得共享锁前要先获得意向共享锁,获得排它锁前要先获得意向排它锁。
意向排它锁互相之间是兼容的。

6 插入意向锁(Insert Intention Locks)

插入意向锁锁的是一个点,是一种特殊的间隙锁,用于并发插入。
插入意向锁和间隙锁互斥。插入意向锁互相不互斥。

7 自增锁(Auto-inc Locks)

自增锁用于事务中插入自增字段。5.1版本前是表锁,5.1及以后版本是互斥轻量锁。
自增所相关的变量有:

auto_increment_offset,初始值

auto_increment_increment,每次增加的数量

innodb_autoinc_lock_mode,自增锁模式

其中:

innodb_autoinc_lock_mode=0,传统方式,每次都产生表锁。此为5.1版本前的默认配置。

innodb_autoinc_lock_mode=1,连续方式。产生轻量锁,申请到自增锁就将锁释放,simple insert会获得批量的锁,保证连续插入。此为5.2版本后的默认配置。

innodb_autoinc_lock_mode=2,交错锁定方式。不锁表,并发速度最快。但最终产生的序列号和执行的先后顺序可能不一致,也可能断裂。

锁组合

首先我们要知道对于MySQL有两种常规锁模式

LOCK_S(读锁,共享锁)

LOCK_X(写锁,排它锁)

最容易理解的锁模式,读加共享锁,写加排它锁.

有如下几种锁的属性

LOCK_REC_NOT_GAP (锁记录)

LOCK_GAP (锁记录前的GAP)

LOCK_ORDINARY (同时锁记录+记录前的GAP 。传说中的Next Key锁)

LOCK_INSERT_INTENTION(插入意向锁,其实是特殊的GAP锁)

锁的属性可以与锁模式任意组合。例如.

lock->type_mode 可以是Lock_X 或者Lock_S

locks gap before rec 表示为gap锁:lock->type_mode & LOCK_GAP

locks rec but not gap 表示为记录锁,非gap锁:lock->type_mode &
LOCK_REC_NOT_GAP

insert intention 表示为插入意向锁:lock->type_mode & LOCK_INSERT_INTENTION

waiting 表示锁等待:lock->type_mode & LOCK_WAIT

锁之间兼容性

X 排他锁
S 共享锁
IX 意向排他锁
IS 意向共享锁

X IX S IS
X
IX 兼容 兼容
S 兼容 兼容
IS 兼容 兼容 兼容

死锁解锁

InnoDB存储引擎会选择回滚undo量最小的事务

参考

并发insert死锁验证
MySQL DELETE 删除语句加锁分析
死锁看这里

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容