数据库死锁引发的掉单问题排查

建议手动复现一下,和只看是两码事、、、、、、、、、、、

1、问题描述

           场景:订单支付成功后用订单号批量通知修改计息状态发生死锁

2、前提:数据库表订单号varchar类型唯一索引跟图中name字段相同

数据库死锁引发的掉单问题排查_第1张图片

3、操作流程

update book set platform_version=400 where name=1111

由于修改订单为批量操作,并且为一条一条修改库表

4、分析:name为唯一索引应该锁行修改,为什么会发生死锁,猜想肯定是两个事物相互依赖导致

分析一show engine innodb status;命令

 数据库死锁引发的掉单问题排查_第2张图片

第一部分,关键词是:

(1)Transaction 1,事务105528;

(2)在执行update book set platform_version=400 where name=2222

(3)正在等待锁释放(waiting for this lock to be granted),记录锁(record locks),主键索引上(index primary),互斥锁(lock_mode X),物理记录(physical record),asc 3333;

分析完这个最近一次死锁后还是有疑问:2222唯一索引干嘛还要拿3333的锁,闹腾、、、、、

分析二explain小工具查看执行计划

数据库死锁引发的掉单问题排查_第3张图片

select_type:UPDATE

这是一个简单类型的SQL语句,不含子查询或者UNION。

type:index

访问类型,即找到所需数据使用的遍历方式,潜在的方式有:

(1)ALL(Full Table Scan):全表扫描;

(2)index:走索引的全表扫描;

(3)range:命中where子句的范围索引扫描;

(4)ref/eq_ref:非唯一索引/唯一索引单值扫描;

(5)const/system:常量扫描;

(6)NULL:不用访问表;

这里为什么是index,唯一索引不应该是走单条吗,怎么会扫全表

key: PRIMARY

实际使用索引:这个是走的主键索引

ref:NULL

哪些列,或者常量用于查找索引上的值,我们条件name说明索引没好使、、、、

rows:7

找到这条数据预读了7行,,尴尬,这不正是全表吗

到这里很明显我们的name唯一索引并 没有起作用,为什么、、、、、

 

这个时候回到表结构name 是varchar类型,而sql

update boot set platform_version=400 where name=3333;

name用的number类型,,,

类型转换,导致锁升级,造成全表扫、、、

重新执行sql:

果然印证了猜想:

(1)type:range,变为了走索引的字符串比对,范围扫描;

(2)possible_keys:index_name,通过index_name索引找到了记录;

(3)key:index_name,实际使用index_name索引;

(4)ref:const,使用了常量' 3333'进行比对;

(5)rows:1,预估读取行数是1;

 

就本例而言:需要注意字符串与整数之间的强制类型转换,有时候少一个引号,就会使得行锁升级为表锁。

 

死锁是MySQL中非常难调试的问题,常见的思路与方法有:

(1)通过多终端模拟并发事务,复现死锁;

(2)通过show engine innodb status; 可以查看事务与锁的信息;

(3)通过explain可以查看执行计划

公众号主要记录各种源码、面试题、微服务技术栈,帮忙关注一波,非常感谢

 

你可能感兴趣的:(故障复盘)