一个InnoDB死锁案例

用来做注册失败设备离线重试的服务,上线之后偶尔会打出这样的ERROR日志:

2019-07-24 16:05:56|DevRegisterRetryHandler.go:144|ERROR|Error 1213: Deadlock found when trying to get lock; try restarting transaction

更新数据库的操作最后是成功的,分析可能是因为这个服务两个节点都在做重试,对同一行记录并发进行读取及更新时出现冲突导致,查了一下资料:

MySQL 5.7 Reference Manual / ... / An InnoDB Deadlock Example

14.7.5.1 一个 InnoDB 死锁示例

首先, 客户端 A 创建一张表并且插入一条数据,然后开始一个事务。在这个事务中, 通过在共享模式下进行SELECT操作,A 在此行持有一个 S 锁:

mysql> CREATE TABLE t (i INT) ENGINE = InnoDB;
Query OK, 0 rows affected (1.07 sec)

mysql> INSERT INTO t (i) VALUES(1);
Query OK, 1 row affected (0.09 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE;
+------+
| i    |
+------+
|    1 |
+------+

接下来, 客户端B也开始一个事务,并试图删除刚刚说的那一行:

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> DELETE FROM t WHERE i = 1;

这个DELETE 操作需要一个 X 锁。由于和客户端A持有的 S 锁冲突,客户端B无法立刻得到这个锁,于是客户端B阻塞,等待这个锁。

最后,客户端A也试图删除这一行:

mysql> DELETE FROM t WHERE i = 1;
ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction

由于客户端A需要一个X锁来删除这行,死锁就发生了。这个锁请求没法成功,因为此时客户端B还在等客户端A释放锁S以拿到锁X。客户端A也没法将S锁升级成X锁,因为客户端B对X锁的请求排在更前面。结果就是InnoDB在其中一个客户端抛出错误,并释放它持有的锁。

ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction

这样另一个客户端就可以得到锁,成功删除这一行。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券