# 简述

现在大多项目都是使用RC事务隔离级别, 同时将binlog format设置为row. RR下死锁相对较多, 且因为next-key lock, 容易导致死锁, 而对RC下的死锁, 认识得比较浅显, 由于最近正好碰到一些场景, 故将其整理, 一起交流学习.

# 实战场景

所有场景均在read committed事务隔离级别

# 表结构与数据

# 与其他专题演示表结构一样
# 表t, id为主键, c为索引(normal), d什么都不是
# 表数据: (0,0,0),(5,5,5),(10,10,10),(15,15,15),(20,20,20),(25,25,25);

# 场景一, update顺序导致的死锁

session A session B
T1 begin;
update t set d=100 where id=5;
(ok)
T2 begin;
update t set d=200 where d=10; (ok)
T3 update t set d=200 where d=10;
(block)
T4 update t set d=100 where id=5;
(Deadlock found...)

场景一分析

本例由于两个事务都要更新两行数据, 但由于update顺序问题, 导致在T3,T4时刻互相锁住, T3被T2锁住, T4被T1锁住, 最后死锁检测报错.

场景一解决方案

  1. 保证执行顺序. 如果是由于同一个逻辑导致的死锁, 那保证顺序一般没有问题, 如果执行逻辑不在同一处, 需要看业务逻辑是否能保证顺序.
  2. 锁住执行语句块, 可以使用分布式锁之类方式保证.

# 场景二, 普通索引与主键索引相互等导致死锁

session A session B
T1 begin;
update t set d=100 where id=5;
(ok)
T2 update t set d=200 where c=5;
(block)
T3 update t set d=300 where c=5;
(ok)
T2时刻的update从block变为Deadlock found...

场景二分析

  1. T1, update锁住id=5的主键索引.
  2. T2, 查询条件c=5为索引查询, 会根据索引c找到对应的id, 同时将两者锁住, 但是由于session A已经锁住id=5, 因此只锁住c=5的索引, 等待获取id=5的主键锁(被阻塞).
  3. T3, update索引c=5的行, 由于检测到session B已经锁住c=5的索引, 因此发现session A和session B死锁, 致使session B直接抛错不执行, session A语句执行成功.

场景二解决方案

尽量保证使用update语句都是基于主键查询.

修改于: 8/11/2022, 3:17:56 PM