数据库死锁问题多半出在加锁顺序,这话不是替数据库开脱。你去翻 SHOW ENGINE INNODB STATUS 里 LATEST DETECTED DEADLOCK 那一段,十有八九能看到两段几乎对称的语句,各自握着一把锁,又都在等对方手里那把。数据库做的事情只是把僵局掀到桌面上,真正埋线的是业务代码里两段逻辑走了不同的方向。所以排查死锁,我不太喜欢一上来就翻参数,先看顺序更划算。

数据库死锁问题多半出在加锁顺序:先把现场还原出来
应用日志里通常只剩一句 Deadlock found when trying to get lock; try restarting transaction,错误码 1213,信息量小得可怜。真正的现场藏在 SHOW ENGINE INNODB STATUS\G 输出里。那一段会告诉你两个事务各自的最后一条语句、持有哪些锁、在等哪把锁、最后回滚了谁。有个细节特别容易看漏:段里显示的往往是事务最后一次执行的语句,不一定是它开头那条,所以别拿它当整条链路的全貌,得回头对着业务代码把顺序拼出来。想看得更细,MySQL 8.0 直接查 performance_schema.data_locks 和 data_lock_waits,一张锁一行,等待关系画得明明白白;5.7 及更早的版本就去 information_schema 里看 INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS。这几张表的字段含义以官方文档为准,别记混。
没有索引的 UPDATE,锁的范围会自己长大
本来只想改一行,结果锁出一片,这种事太常见了。更新条件没走索引,扫描到的行全被锁住,两个事务各自锁住一片区域,一旦有重叠就开始互相等。很多人盯着 SQL 的语法翻来覆去看,就是不去跑一次 EXPLAIN。加索引不解决顺序问题,但能把锁的面积收窄,撞上的概率自然低。间隙锁同理,可重复读隔离级别下,范围条件很容易带上 next-key lock,连区间里还不存在的行都给锁了,这种锁在日志里看着像凭空冒出来的,实际是范围查询的副作用。
长事务把窗口撑大
事务里夹着一次外部接口调用、一次文件写入、甚至一段 sleep,锁就被举在半空中。两个都慢的事务撞在一起,概率跟持锁时间基本成正比。把事务改短、把非数据库操作挪到事务外面,是最省事的一招,不用改业务逻辑,效果还立竿见影。还有一个常见毛病是循环里一条条更新,几百次小事务挨着跑,不如一次批量按排好的顺序处理。锁的时间窗窄了,死锁的出现频率会明显低下去。
统一加锁顺序,落地就几条规矩
最典型的例子是转账,A 转 B 走 A 到 B,B 转 A 走 B 到 A,两边同时跑就互相等。规矩可以很土:按账户 id 的大小升序加锁,谁都不许反着来。批量更新也一样,先把主键列表排序,再按顺序逐条改。跨表操作定一个表级别的顺序,所有人遵守,写进代码规范里,code review 的时候专门盯这一条。听着死板,可比事后半夜爬起来看日志舒服。新同事进来第一周就把这条讲清楚,比出事之后再复盘便宜得多。
重试不是遮羞布,得配幂等
死锁会被 InnoDB 自动检测出来并回滚其中一个事务,所以应用层做重试是合理的补救。但重试要有随机退避、要有次数上限,业务本身还得幂等,不然重复扣款、重复发货这类问题比死锁本身更难收拾。另一个态度问题:别把死锁当偶发事件忽略过去。日志里数一数,一周出现几十次,那就不是运气,是结构性的顺序冲突,重试只是在掩盖症状。建议给 1213 单独打个计数指标,涨上去就该有人看。
隔离级别和锁超时,别一刀切
把隔离级别从可重复读降到读已提交,确实能少掉大量间隙锁,很多高并发写入的场景这么干能立刻见效。不过前提是业务能接受不可重复读的语义差异,为了不死锁牺牲一致性就不值了。同样,把 innodb_lock_wait_timeout 调大只是把报错往后推,锁等待超时是 1205,跟死锁不是一回事,别混着治。还有 SELECT … FOR UPDATE 这类显式加锁,范围写宽了等于自己制造冲突,用它之前先想清楚锁的到底是哪几行。
真上手排查,按这个顺序走
一,先确认日志里是死锁还是普通锁等待超时,两者的处理方向不同。二,从 InnoDB 状态输出里拎出两条 SQL 和它们各自的加锁次序。三,对这两条 SQL 跑 EXPLAIN,看索引有没有被用上。四,检查事务里有没有夹带网络请求或者循环更新。五,看应用层的重试和幂等是不是齐备。六,把 1213 的次数做成监控曲线,改完代码看它降没降。这个顺序走一遍,多数问题能定位到具体那几行代码,而不是停在一句“数据库好像不太行”。
加锁顺序这件事,本质上是设计期该定下来的规矩,等死锁报出来再回头改,成本翻好几倍。代码改完别急着上线,在预发环境跑一遍并发压测,把两个方向的请求同时打上去,看看日志里还有没有 1213。顺手把那条规范贴到项目文档里,下一个人就不用重新踩一遍了。
【版权声明】:本站内容若无意侵犯到您的权利,请及时与我们联系将尽快删除相关内容!