一个InnoDB死锁的例子

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;
X锁无法获取,因为S锁还没释放。

最后,客户端A也试图从表中删除该行:
mysql> DELETE FROM t WHERE i = 1;
然后报错了
ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction

死锁发生在这里,因为客户端A需要一个 X锁来删除该行。但是,该锁请求不能被授予,因为客户端B已经有一个X锁的请求,正在等待客户端A释放其S 锁。
总结:锁请求就像一个队列,有先来后到之分。

InnoDB中死锁自动被检测出,并选择代价较小的事务进行回滚以打破死锁。


最小化和处理死锁的方法

  • SHOW ENGINE INNODB STATUS命令来确定最近死锁的原因。
  • 开启innodb_print_all_deadlocks 配置,这样可以在error log中看到所有的死锁信息而非最近的一个,结束调试后记得关闭
  • 如果一个事务由于死锁而失败,仅仅需要重新执行一下它即可。
  • 把事务变得短小精悍
  • 立刻提交事务
  • 不要使用锁定读,使用第一点的隔离级别。
  • 用一定的顺序来执行复杂的事务
  • 建立合适的索引,让你的query扫描尽量少的行。这样可以少锁一些行。使用EXPLAIN SELECT看看索引是否合适。
  • 少锁一些,用快照读,而不是当前读。



下一篇:InnoDb配置

成为你想看到的世界变革力量

创建者:万乐荣
最后更新时间 : 2017年11月10日 18:43

评论