一个 MySQL 死锁案例分析

Reading time ~1 minute

对 MySQL 死锁不是特别擅长,业务中遭遇了,就尝试分析一下了。

首先有篇不错的关于 MySQL 加锁机制的文章以及一个很有意思的 INSERT 死锁例子,Mark 一下。

业务需求,写出了类似这样的 SQL:

1
2
INSERT INTO site(third_party_id, data) VALUES(%s, %s)
ON DUPLICATE KEY UPDATE data = %s

其中 site 表主键 id 自增,唯一索引 third_party_id

SQL 目的很简单,就是从一张三方表中导数据到自己业务的一张表中间。 就这样简单的一句 SQL,没有复杂的事务,只是会出现大量的并发。

并发跑起来之后,很快就会发现程序报出大量死锁,看下死锁记录,可以抓到一条死锁信息大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
*** (1) TRANSACTION:
TRANSACTION 4185716323, ACTIVE 0.021 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1184, 3 row lock(s)
LOCK BLOCKING MySQL thread id: 252573051 block 235860837
MySQL thread id 235860837, OS thread handle 0x7f6fcc2a4700, query id 1581628064 172.16.7.71 prod update
...
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 794 page no 8 n bits 128 index `PRIMARY` of table ``.`` trx id 4185716323 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 35; compact format; info bits 0
...
*** (2) TRANSACTION:
TRANSACTION 4185716326, ACTIVE 0.014 sec inserting
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1184, 3 row lock(s)
MySQL thread id 235818269, OS thread handle 0x7f6d88fbe700, query id 1581628068 172.16.7.71 prod update
...
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 794 page no 8 n bits 128 index `PRIMARY` of table ``.`` trx id 4185716326 lock_mode X locks gap before rec
Record lock, heap no 2 PHYSICAL RECORD: n_fields 35; compact format; info bits 0
...
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 794 page no 8 n bits 128 index `PRIMARY` of table ``.`` trx id 4185716326 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 35; compact format; info bits 0

这里事务 2 由于是 INSERT with ON UPDATE,在 constraint check 中会直接上 X 锁,参见这里。 而同时我们这里主键让其自增,针对 third_party_id 字段来做的操作,于是导致主键索引产生的 gap 锁而不是 record 锁。 接下来,与这里类似,事务2在有了 X gap 锁之后,又申请了个其实没有必要的 insert intention lock, 这一请求排在了事务1的 insert intention lock 请求之后,于是带来了死锁。

这里对于这一导入数据的业务需求,其实一次导入中并不会出现多个相同的 third_party_id, 于是我们直接 SELECT 出来,判断一下再 INSERT 或者 UPDATE 即可,两条语句间也不需要做事务,因为不会中途被改掉。

挂载网络文件夹后网络故障时文件操作命令卡死

挂载 NFS 或者 Samba 的时候,经常会由于网络故障导致挂载好的链接断掉。此时如果尝试进行 ls、cd、df 等各种命令,只要与此目录沾上边,就会卡住。如果使用了类似 oh-my-zsh 这种配置的,只要在网络目录中,弹出命令提示符前就会直接卡住。这个时候第一反应就是...… Continue reading

路由折腾记 第四弹

Published on September 02, 2017