我在网发布的免费视频讲解 MySQL 8.0 版本新特性。
在 MysqL 中,如果两个不同的事务在执行时,互相持有了对方所需的锁,此时由于它们都在等待某个资源,永远不会释放自己获得的锁,因此就会产生死锁(deadlock)。
以下是一个产生死锁的示例。首先,在客户端 A 中创建一个表 t,它只有一行数据。然后开始一个事务,并且通过共享查询模式获取该行数据上的 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 中开始另一个事务,并且尝试删除该行数据:
MysqL> START TRANSACTION;query OK, 0 rows affected (0.00 sec)MysqL> DELETE FROM t WHERE i = 1;
删除 *** 作需要获得一个排他锁(X)。由于客户端 A 已经获得了一个 S 锁,客户端 B 的锁请求需要进入锁的等待队列,因此客户端 B 被阻塞。
最后,在客户端 A 中也尝试删除该行数据:
MysqL> DELETE FROM t WHERE i = 1;query OK, 1 row affected (0.00 sec)
同时,在客户端 B 返回以下错误信息:
MysqL> DELETE FROM t WHERE i = 1;ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
此时产生了死锁,因为客户端 A 需要获得一个 X 锁才能删除该行数据。然而,客户端 A 无法获得该锁,因为客户端 B 已经请求了一个 X 锁,并且等待客户端 A 释放该数据行上的 S 锁。客户端 A 上的 S 锁也无法升级为 X 锁,因为客户端 B 的 X 锁请求优先级更高。结果就是,InnoDB 将会在某个客户端产生错误,并且释放它所获取的锁。
至此,另一个客户端(A)能够获得请求的锁,并且删除该数据行。
通过以上示例可以看出,当启用了死锁检测时(默认设置),InnoDB 自动执行事务的死锁检测,并且回滚一个或多个事务以解决死锁。InnoDB 尝试回滚更小的事务,事务的大小由它所插入、更新或者删除的数据行数决定。
在 MysqL 8.0 中,增加了一个新的动态变量:innodb_deadlock_detect,可以用于控制 InnoDB 是否执行死锁检测。该参数的默认值为 ON,即打开死锁检测。
MysqL> show variables like 'innodb_deadlock_detect';+------------------------+-------+| Variable_name | Value |+------------------------+-------+| innodb_deadlock_detect | ON |+------------------------+-------+1 row in set (0.00 sec)
对于高并发的系统,当大量线程等待同一个锁时,死锁检测可能会导致性能的下降。此时,如果禁用死锁检测,而改为依靠参数 innodb_lock_wait_timeout 执行发生死锁时的事务回滚可能会更加高效。
接下来,先在客户端 A 中关闭死锁检测:
MysqL> set global innodb_deadlock_detect=off;query OK, 0 rows affected (0.04 sec)
重复上文中的示例,在客户端 A 中执行以下 *** 作:
MysqL> show variables like 'innodb_lock_wait_timeout';+--------------------------+-------+| Variable_name | Value |+--------------------------+-------+| innodb_lock_wait_timeout | 50 |+--------------------------+-------+1 row in set (0.00 sec)MysqL> INSERT INTO t (i) VALUES(1);query OK, 1 row affected (0.00 sec)MysqL> START TRANSACTION;query OK, 0 rows affected (0.00 sec)MysqL> SELECT * FROM t WHERE i = 1 FOR SHARE;+------+| i |+------+| 1 |+------+
在客户端 B 执行删除 *** 作:
MysqL> START TRANSACTION;query OK, 0 rows affected (0.00 sec)MysqL> DELETE FROM t WHERE i = 1;
客户端 B 等待 X 锁,仍然被阻塞。再回到客户端 A,尝试删除该行数据:
MysqL> DELETE FROM t WHERE i = 1;query OK, 1 row affected (37.87 sec)
此时客户端 B 不会提示死锁错误,而是等待 50 s (innodb_lock_wait_timeout 设置值)后提示等待超时:
MysqL> DELETE FROM t WHERE i = 1;ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
同时客户端 A 在此之后成功删除数据,可以看到删除 *** 作的时间为 37.87 s 。这个时间依取决于最后在客户端 A 中输入删除语句的时间。
通常来说,应该启用死锁检测,并且在应用程序中尽量避免产生死锁,同时对死锁进行相应的处理,例如重新开始事务。
只有在确认死锁检测影响了系统的性能,并且禁用死锁检测不会带来负面影响时,可以尝试关闭 innodb_deadlock_detect 选项。另外,如果禁用了 InnoDB 死锁检测,需要调整参数 innodb_lock_wait_timeout 的值,以满足实际的需求。
官方文档:MySQL 8.0 Reference Manual
总结以上是内存溢出为你收集整理的MySQL 8.0 新特性之死锁检测控制全部内容,希望文章能够帮你解决MySQL 8.0 新特性之死锁检测控制所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)