InnoDB 死锁会回滚整个受害事务
当多个事务形成不兼容锁请求的等待环时,就发生 InnoDB 死锁。开启死锁检测后,引擎选择一个受害者,回滚它的整个事务,使其他参与者能够继续。应用收到错误 1213(SQLSTATE 40001)。若只重试报错的那条语句,就会漏掉同一事务中更早执行、但已经一起被回滚的工作。 坏掉的样子 不能给所有锁错误套用同一种回滚规则。默认关闭超时整笔回滚时,锁等待超时只撤销正在等待的那条语句,事务内此前执行的语句仍然待决。开启对应服务端选项后,超时才会改为回滚整个事务。死锁本来就会回滚整个受害事务,不能把默认超时的语句级行为误套在死锁上。 受害者选择也不保证某个固定会话永远输。应用必须检查真实错误,再以有界次数重试完整逻辑事务。不可逆的外部副作用如果没有单独去重契约,不应跟着数据库事务自动重放。提交回复途中连接断开,也不等于已经得到死锁回滚的确定结论;错误类型与已知事务结果需要分开判断。 死锁受害者可能是任一参与者。超时对照要求关闭超时整笔回滚,并在全部会话使用可重复读;所示结果须人工检查。 快速验证 历史三会话测试保持原样,本轮没有重跑。保留的审阅记录报告当时无法使用 MySQL,且未在保留的验证文件中找到原始 MySQL 日志;下述版本与受害者选择仍归属于原文报告。 在可丢弃的 MySQL 8.4 实例上打开甲、乙和观察者三个全新管理客户端,先确认三个会话均处于自动提交模式,且隔离级别为 REPEATABLE READ;修改过的服务器默认值可能改变可见性,或让后面的读取也进入锁等待。关闭客户端自动重新连接,保持会话身份稳定。既有运行记录中的版本是 MySQL 8.4.11。观察者先创建尚未使用的实验模式,并核对死锁检测开启、超时采用默认语句级回滚: CREATE DATABASE xbcatm_deadlock_lab; USE xbcatm_deadlock_lab; CREATE TABLE accounts (id INT PRIMARY KEY, balance INT NOT NULL) ENGINE=InnoDB; INSERT INTO accounts VALUES (1,100),(2,100); SELECT @@innodb_deadlock_detect,@@innodb_rollback_on_timeout; 两个设置应分别为一和零。甲开始一项给两个账户各增加十的操作,先锁住第一个账户: ...