mysql死锁分析工具show engine innodb status

 参考文章

《记录一次MySQL死锁的分析与解决过程》

《mysql之show engine innodb status解读》

《把MySQL中的各种锁及其原理都画出来》

        写在开头:本文为学习后的总结,可能有不到位的地方,错误的地方,欢迎各位指正。

前言

        在之前的文章(《mysql运维脚本与个人理解》)中介绍了Mysql运维的几个简单脚本。

但最后的死锁根因分析因为缺少实践经验与分析文章搁置了,这里来补上。

目录

前言

        一、行锁的实现

        二、show engine innodb status的介绍与使用

        1、内容介绍        

        2、内容分析

        3、解决办法


一、行锁的实现

        在《mysql之事务、锁、隔离级别与MVCC》一文中,介绍了行锁是基于索引的,只有在命中索引时才会出现行锁。但实际上并没有解释清楚行锁是怎么做到精确锁住每一行数据的,也因此带来了一些疑问,比如:2个单列索引,假设事务A通过索引1获取了行锁,既然是加在索引上的锁,那也就意味着,只是锁住了索引1,而索引2上是没有限制的,是否意味着事务2可以通过索引2改动这一行的数据?

        要回答这个问题,我们首先回顾下《mysql库中索引的基础概念》中介绍的InnoDB的索引物理结构,二级索引通过主键索引访问数据。

        

        重点来了,行锁实际上也会判断命中的索引是否为主键,如不是,则会在该命中的索引以及主键索引上加锁,如下图所示。因此,开头提到的问题自然是无法修改的,因为最后的最后有主键索引把关。

        (图片来源《把MySQL中的各种锁及其原理都画出来》

二、show engine innodb status的介绍与使用

        很多时候我们的死锁出现后立马就会回滚,无法通过报错日志直接定位到异常的sql语句,这里就需要使用mysql为我们提供的分析工具show engine innodb status

        1、内容介绍        

        下面是前几天刚遇到的问题,正好拿来做介绍。

------------------------
LATEST DETECTED DEADLOCK
------------------------
2021-08-18 14:04:16 0x7f29f2ef5700
*** (1) TRANSACTION:
TRANSACTION 14235673, ACTIVE 0 sec starting index read
mysql tables in use 3, locked 3
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 10089425, OS thread handle 139823504013056, query id 157595766 10.75.34.61 dbroot updating
update table1 set aaa='4',bbb='121',ccc='合格',ddd='0',eee='' where bd='6f174b50-8d32' and dw='1e0adeed-3f0d-450a' and tag =1
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 14096 page no 57 n bits 272 index bd_index of table `mydatabase`.`table1` trx id 14235673 lock_mode X locks rec but not gap waiting
Record lock, heap no 114 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 30; hex 36663137346235302d386433322d343763362d613062622d633962333238; asc 6f174b50-8d32-47c6-a0bb-c9b328; (total 36 bytes);
 1: len 30; hex 30636538343031652d653061662d343733362d623031372d626464623663; asc 0ce8401e-e0af-4736-b017-bddb6c; (total 36 bytes);

*** (2) TRANSACTION:
TRANSACTION 14235674, ACTIVE 0 sec fetching rows
mysql tables in use 3, locked 3
9 lock struct(s), heap size 1136, 6 row lock(s), undo log entries 2
MySQL thread id 10089424, OS thread handle 139818146158336, query id 157595768 10.75.34.61 dbroot updating
update table1 set aaa='10',bbb='12',ccc='合格',ddd='0',eee='' where bd='6f174b50-8d32' and dw='ffb27cdc-ba40-4e16' and tag =1
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 14096 page no 57 n bits 272 index bd_index of table `mydatabase`.`table1` trx id 14235674 lock_mode X locks rec but not gap
Record lock, heap no 114 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 30; hex 36663137346235302d386433322d343763362d613062622d633962333238; asc 6f174b50-8d32-47c6-a0bb-c9b328; (total 36 bytes);
 1: len 30; hex 30636538343031652d653061662d343733362d623031372d626464623663; asc 0ce8401e-e0af-4736-b017-bddb6c; (total 36 bytes);

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 14096 page no 57 n bits 272 index bd_index of table `mydatabase`.`table1` trx id 14235674 lock_mode X locks rec but not gap waiting
Record lock, heap no 105 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 30; hex 36663137346235302d386433322d343763362d613062622d633962333238; asc 6f174b50-8d32-47c6-a0bb-c9b328; (total 36 bytes);
 1: len 30; hex 66396262333533362d356137342d343633352d386335632d323666356537; asc f9bb3536-5a74-4635-8c5c-26f5e7; (total 36 bytes);

*** WE ROLL BACK TRANSACTION (1)

        TRANSACTION 14235673, ACTIVE 0 sec starting index read   

        事务14235673,ACTIVE 0 sec表示事务处于活跃状态0s,starting index read表示正在使用索引读取数据行 

        mysql tables in use 3, locked 3

        事务1正在使用3个表,且涉及锁的表有3个

        LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1

        这行表示在等待3把锁,占用内存1136字节,涉及2行记录,如果事务已经锁定了几行数据。

        MySQL thread id 10089425, OS thread handle 139823504013056, query id 157595766 10.75.34.61 dbroot updating

        该事务的线程ID信息,操作系统句柄信息,连接来源、用户等

        update table1 set aaa='4',bbb='121',ccc='合格',ddd='0',eee='' where bd='6f174b50-8d32' and dw='1e0adeed-3f0d-450a' and tag =1

        正在等待行锁的sql

        *** (1) WAITING FOR THIS LOCK TO BE GRANTED:

        *** (2) HOLDS THE LOCK(S):

        正在等待的锁、目前保存的锁

        RECORD LOCKS space id 14096 page no 57 n bits 272 index bd_index of table `mydatabase`.`table1` trx id 14235673 lock_mode X locks rec but not gap waiting

        等待的锁是一个record lock,空间id是14096,页编号为57,大概位置在页的272位处,锁发生在表mydatabase.table1的bd_index 索引上,是一个X锁,但是不是gap lock。 waiting表示正在等待锁

        Record lock, heap no 114 PHYSICAL RECORD: n_fields 2; compact format; info bits 0

        这行表示record lock的heap no 位置(可以用来对照事务2控制住的锁)

        *** WE ROLL BACK TRANSACTION (1)

        回滚了事务1

        2、内容分析

        通过观察事务1等待的行锁在堆中的位置,与事务2获取到的锁在堆中的位置,确定了事务2手中有事务1想要获取的锁。再来分析涉及到的sql,我们发现产生冲突的是同1条sql,在table1这张表中,bd与dw是一对多的关系(这里假设是1:10),也就导致要获取bd_index(基于bd的单列索引)时,要获取到全部的10个bd上的索引,也就导致产生了争用。

        3、解决办法

        问题的原因在于单列索引扫描的范围过大,要获取到全部10个db上的索引,那么我们只要减小获取锁的范围就好了,这里就到了我们的组合索引展示优势的地方了。我们建立bd,dw上的组合索引,这样一来,事务1和事务2中的2条sql都只要获取到自身涉及到的那1条组合索引即可。

create index complex_index on table1(bd,dw);

要找到 `SHOW ENGINE INNODB STATUS` 命令输出的 InnoDB 存储引擎状态信息中某个线程所在的事务 ID,可以按照以下步骤进行: 1. 执行 `SHOW ENGINE INNODB STATUS\G` 命令,将 InnoDB 存储引擎状态信息以详细的方式输出到终端或客户端。 2. 在输出的状态信息中,找到 `LATEST FOREIGN KEY ERROR` 或 `LATEST DETECTED DEADLOCK` 部分。在这个部分中,通常会列出最近发生的死锁或外键错误的相关信息。 3. 在这个部分中,找到被称为“TRANSACTION”的部分。该部分会列出当前正在运行或等待的事务的详细信息,包括事务 ID、事务状态、锁定的对象等。 4. 在事务列表中找到您要查找的线程所在的事务。您可以根据线程 ID 在列表中查找到该事务,并在该事务的信息中找到事务 ID。 例如,以下是 `SHOW ENGINE INNODB STATUS\G` 命令输出的状态信息中事务列表的示例: ``` ------------ TRANSACTIONS ------------ Trx id counter 0 123456 Purge done for trx's n:o < 0 123455 undo n:o < 0 0 History list length 2 LIST OF TRANSACTIONS FOR EACH SESSION: ---TRANSACTION 0 123455, ACTIVE 10 sec starting index read mysql tables in use 1, locked 1 LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s) MySQL thread id 123, OS thread handle 0x7fb3b5c1d700, query id 1234 localhost root updating UPDATE `test` SET `value` = '123' WHERE `id` = 1 ------- TRX HAS BEEN WAITING 10 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 1234 page no 1 n bits 72 index `PRIMARY` of table `test` trx id 0 123455 lock_mode X waiting Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0 ``` 在这个例子中,事务列表中包含了当前正在运行的事务和等待锁定的事务。您可以根据线程 ID 查找到对应的事务,例如这里线程 ID 为 `123`,则可以在事务列表中找到该线程所在的事务,该事务 ID 为 `0 123455`。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值