这两天遇到一个死锁的问题,两个程序同时使用 delete 删除两张表,A 程序使用的是T1 LEFT JOIN T2
,B 程序使用的是T2 INNER JOIN T1
。此时会出现死锁。
因为两个程序的 SQL 连结表的时候,T1 、T2 表的顺序不同,所以尝试把 B 程序T2 INNER JOIN T1
调整成T1 INNER JOIN T2
,依然会出现死锁。
最后把 B 程序也改成 LEFT JOIN:T1 LEFT JOIN T2
,多次尝试后没有发生死锁。但是如果把 B 程序改成T2 LEFT JOIN T1
的话,会非常容易发生死锁。
而且SHOW ENGINE INNODB STATUS
查看死锁信息信息的时候明确可以看到是record lock
。
所以我想问的是,是不是T1 LEFT JOIN T2
的时候,会优先把 T1 里的所有 record 锁上,然后再锁 T2 里的 record 。
但是T1 INNER JOIN T2
的时候,顺序就不一定了。
我猜测的是:
有人知道是不是这么回事吗?
1
zhanglintc OP 已经破案了,可以参考这篇文章: https://blog.csdn.net/mccand1234/article/details/105455626
总结一下就是,正常情况下,`T1 INNER JOIN T2`的确是先锁左表 T1 ,然后锁右表 T2 。 但是当 T1 表的数据量**远大于**T2 表的情况下,优化器可能会先读取 T2 表,然后读取 T1 表。此时读取顺序就跟书写顺序无关了。在我这个 case 里就表现为无论如何都会发生死锁。 然后可以通过`STRAIGHT_JOIN`替换`INNER JOIN`来强制保证先锁左表,再锁右表。 还有就是 LEFT JOIN 是可以保证先锁左表再锁右表的。 最后想看锁表顺序可以通过`EXPLAIN`来看。比如`EXPLAIN SELECT T1 AS tab1 INNER JOIN T2 AS tab2`查看先读取的 T1 还是 T2 表。先读哪个就会先锁哪个。 |