为了减少死锁的可能性,保持插入或更新数据的事务足够小,避免它们长时间保持打开状态;当不同的事务更新多个表或大范围的行时,在每个事务中使用相同的操作顺序;在 SELECT ... FOR UPDATE and UPDATE ... WHERE 语句上创建索引。
当死锁检测被打开时(默认),InnoDB会检测条件并回滚其中一个事务,InnoDB会回滚较小的事务(insert、update、delete语句较少)。如果事务检测innodb_deadlock_detect被关闭时,InnoDB依赖innodb_lock_wait_timeout配置来回滚死锁事务。
使用SHOW ENGINE INNODB STATUS 命令可以查看死锁。
一个死锁的例子
事务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 FOR SHARE;
+------+
| i |
+------+
| 1 |
+------+
事务B执行以下语句,因为S锁被事务A持有,事务B等待获取X锁
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> DELETE FROM t WHERE i = 1;
事务A再执行以下语句,因为事务B比事务A更早申请X锁,所以事务A等待事务B,最后发生发生死锁
mysql> DELETE FROM t WHERE i = 1;
ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction