zoukankan      html  css  js  c++  java
  • MySQL 死锁日志分析

    ------------------------
    LATEST DETECTED DEADLOCK
    ------------------------
    2017-09-06 11:58:16 7ff35f5dd700
    *** (1) TRANSACTION:
    TRANSACTION 182335752, ACTIVE 0 sec inserting
    mysql tables in use 1, locked 1
    LOCK WAIT 11 lock struct(s), heap size 1184, 2 row lock(s), undo log entries 15
    MySQL thread id 12032077, OS thread handle 0x7ff35ebf6700, query id 196418265 10.40.191.57 RW_bok_db update
    INSERT INTO bok_task
                     ( order_id ...
    *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 300 page no 5480 n bits 552 index `order_id_un` of table `bok_db`.`bok_task`
        trx id 182335752 lock_mode X insert intention waiting
    *** (2) TRANSACTION:
    TRANSACTION 182335756, ACTIVE 0 sec inserting
    mysql tables in use 1, locked 1
    11 lock struct(s), heap size 1184, 2 row lock(s), undo log entries 15
    MySQL thread id 12032049, OS thread handle 0x7ff35f5dd700, query id 196418268 10.40.189.132 RW_bok_db update
    INSERT INTO bok_task
                     ( order_id ...
    *** (2) HOLDS THE LOCK(S):
    RECORD LOCKS space id 300 page no 5480 n bits 552 index `order_id_un` of table `bok_db`.`bok_task`
        trx id 182335756 lock_mode X
    *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 300 page no 5480 n bits 552 index `order_id_un` of table `bok_db`.`bok_task`
        trx id 182335756 lock_mode X insert intention waiting
    *** WE ROLL BACK TRANSACTION (2)

    日志中列出了死锁发生的时间,以及导致死锁的事务信息(只显示两个事务,如果由多个事务导致的死锁也只显示两个),并显示出每个事务正在执行的 SQL 语句、等待的锁以及持有的锁信息等。

    看事务一的信息:

    * (1) TRANSACTION:
    TRANSACTION 182335752, ACTIVE 0 sec inserting
    ACTIVE 0 sec 表示事务活动时间,inserting 为事务当前正在运行的状态,可能的事务状态有:fetching rows,updating,deleting,inserting 等。

    mysql tables in use 1, locked 1
    LOCK WAIT 11 lock struct(s), heap size 1184, 2 row lock(s), undo log entries 15
    tables in use 1 表示有一个表被使用,locked 1 表示有一个表锁。LOCK WAIT 表示事务正在等待锁,11 lock struct(s) 表示该事务的锁链表的长度为 11,每个链表节点代表该事务持有的一个锁结构,包括表锁,记录锁以及 autoinc 锁等。heap size 1184 为事务分配的锁堆内存大小。
    2 row lock(s) 表示当前事务持有的行锁个数,通过遍历上面提到的 11 个锁结构,找出其中类型为 LOCK_REC 的记录数。undo log entries 15 表示当前事务有 15 个 undo log 记录,因为二级索引不记 undo log,说明该事务已经更新了 15 条聚集索引记录。

    * (1) WAITING FOR THIS LOCK TO BE GRANTED:
    这里显示的是正在等待锁的 SQL 语句,死锁日志里每个事务都只显示一条 SQL 语句,这对我们分析死锁很不方便,我们必须要结合应用程序去具体分析这个 SQL 之前还执行了哪些其他的 SQL 语句,或者根据 binlog 也可以大致找到一个事务执行的 SQL 语句。

    RECORD LOCKS space id 300 page no 5480 n bits 552 index `order_id_un` of table `bok_db`.`bok_task` trx id 182335752 lock_mode X insert intention waiting

    这里显示的是事务正在等待什么锁。RECORD LOCKS 表示记录锁(并且可以看出要加锁的索引为 order_id_un),space id 为 300,page no 为 5480,n bits 552 表示这个记录锁结构上留有 552 个 bit 位(该 page 上的记录数 + 64)。
    lock_mode X 表示该记录锁为排他锁,insert intention waiting 表示要加的锁为插入意向锁,并处于锁等待状态。

    在上面有提到 innodb_status_output_locks 这个系统变量可以开启 InnoDb 的锁监控,如果开启了,这个地方还会显示出锁的一些额外信息,包括索引记录的 info bits 和数据信息等:

    有四种类型的行锁:记录锁,间隙锁,Next-key 锁和插入意向锁。这四种锁对应的死锁日志各不相同,如下:

    记录锁(LOCK_REC_NOT_GAP): lock_mode X locks rec but not gap
    间隙锁(LOCK_GAP): lock_mode X locks gap before rec
    Next-key 锁(LOCK_ORNIDARY): lock_mode X
    插入意向锁(LOCK_INSERT_INTENTION): lock_mode X locks gap before rec insert intention

    * (2) HOLDS THE LOCK(S):
    RECORD LOCKS space id 300 page no 5480 n bits 552 index `order_id_un` of table `bok_db`.`bok_task` trx id 182335756 lock_mode X
    * (2) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 300 page no 5480 n bits 552 index `order_id_un` of table `bok_db`.`bok_task` trx id 182335756 lock_mode X insert intention waiting
    
    事务二和事务一的日志基本类似,不过它多了一部分 HOLDS THE LOCK(S),表示事务二持有什么锁,这个锁往往就是事务一处于锁等待的原因。这里可以看到事务二正在等待索引 order_id_un 

    事务二和事务一的日志基本类似,不过它多了一部分 HOLDS THE LOCK(S),表示事务二持有什么锁,这个锁往往就是事务一处于锁等待的原因。这里可以看到事务二正在等待索引 order_id_un 上的插入意向锁,并且它已经持有了一个 X 锁(Next-key 锁,也有可能是 supremum 上的间隙锁)。

    到这里为止,我们得到了很多关键信息,此时我们可以逆推出死锁发生的原因吗?这可能也是每个开发人员和 DBA 最关心的问题,如何通过死锁日志来诊断死锁的成因?实际上这是非常困难的。

    如果每个事务都只有一条 SQL 语句,这种情况的死锁成因还算比较好分析,因为我们可以从死锁日志里找到每个事务执行的 SQL 语句,只要对这两条 SQL 语句的加锁过程有一定的了解,死锁原因一般不难定位。但也有可能死锁的成因非常隐蔽,这时需要我们对这两条 SQL 语句的加锁流程做非常深入的研究才有可能分析出死锁的根源。

    不过大多数情况下,每个事务都不止一条 SQL 语句,譬如上面的死锁日志里显示的 undo log entries 15,说明执行 INSERT 语句之前肯定还执行了其他的 SQL 语句,但是具体是什么,我们不得而知,我们只能根据 HOLDS THE LOCK(S) 部分知道有某个 SQL 语句对 order_id_un 索引加了 Next-key 锁(或间隙锁)。另外事务二在 WAITING FOR 插入意向锁,至于它和事务一的哪个锁冲突也不得而知,因为事务一的死锁日志里并没有 HOLDS THE LOCK(S) 部分。

    所以,对死锁的诊断不能仅仅靠死锁日志,还应该结合应用程序的代码来进行分析,如果实在接触不到应用代码,还可以通过数据库的 binlog 来分析(只要你的死锁不是 100% 必现,那么 binlog 日志里肯定能找到一份完整的事务一和事务二的 SQL 语句)。通过应用代码或 binlog 理出每个事务的 SQL 执行顺序,这样分析死锁时就会容易很多。

  • 相关阅读:
    明星球队的傲慢
    VS2010测试方面的文章重点
    项目管理有感
    团队从小到大,再到体验团队
    css实现文字渐变
    echarts坐标轴文字过长省略
    mustache+mock
    你有选择的权利
    呵呵,初学者小编
    Centos7升级到OpenSSH_8.8p1、OpenSSL 1.1.1l版本
  • 原文地址:https://www.cnblogs.com/hankyoon/p/14686498.html
Copyright © 2011-2022 走看看