diff --git a/notes/计算机操作系统 - 死锁.md b/notes/计算机操作系统 - 死锁.md index e3788f54..e18b7d71 100644 --- a/notes/计算机操作系统 - 死锁.md +++ b/notes/计算机操作系统 - 死锁.md @@ -262,7 +262,15 @@ INSERT INTO payment VALUES (1025, 50); -- 同样等待事务 1 读 MySQL 8.0 的死锁日志时,两个事务的情况下 (2) 是最后开始等待、使等待关系形成环的事务,(1) 是它在等的事务;(1) HOLDS THE LOCK(S) 列出的就是 (2) 在等的那把锁,反之亦然。 -每个事务只显示当时正在执行的语句,持有的锁可能是更早的语句加的,例如附录 A3 中两个事务显示的都是 INSERT,间隙锁却来自之前的 SELECT ... FOR UPDATE。 +每个事务只显示当时正在执行的语句,持有的锁可能是更早的语句加的,例如附录 A3 中两个事务显示的都是 INSERT,间隙锁却来自之前的 SELECT ... FOR UPDATE。要找出更早的语句,可以按日志中的 MySQL thread id 查 performance_schema.events_statements_history,它默认为每个连接保留最近 10 条语句(MySQL 8.0 实测,见[附录 A3](#a3-先查后插的间隙锁死锁)): + +```sql +SELECT h.EVENT_ID, h.SQL_TEXT +FROM performance_schema.events_statements_history h +JOIN performance_schema.threads t ON t.THREAD_ID = h.THREAD_ID +WHERE t.PROCESSLIST_ID = 4136 -- 死锁日志中的 MySQL thread id +ORDER BY h.EVENT_ID; +``` lock_mode X locks gap before rec 是间隙锁,后面再有 insert intention 的是插入意向锁,在 data_locks 中分别显示为 X,GAP 和 X,GAP,INSERT_INTENTION。 @@ -354,6 +362,19 @@ INSERT INTO payment VALUES (1025, 50) ERROR 1062 (23000) at line 1: Duplicate entry '1024' for key 'payment.PRIMARY' ``` +死锁发生后,按事务 2 的 MySQL thread id 查 events_statements_history(history_size 为 10,这个 consumer 默认开启),能看到它在 INSERT 之前执行的 SELECT ... FOR UPDATE(节选,省略了客户端连接时执行的语句): + +```text +*** (2) TRANSACTION: +MySQL thread id 4136, OS thread handle 6157561856, query id 4812446 localhost root update +INSERT INTO payment VALUES (1025, 50) + +EVENT_ID SQL_TEXT +3 BEGIN +5 SELECT * FROM payment WHERE order_id = 1025 FOR UPDATE +6 INSERT INTO payment VALUES (1025, 50) +``` + 执行 SET GLOBAL innodb_print_all_deadlocks = ON 后,再执行一遍可重复读的例子,错误日志中同样记录了两个事务持有的间隙锁和等待的插入意向锁(lock_mode X locks gap before rec insert intention waiting)。 ### A4 死锁线程的状态与 CPU 时间