对账任务 update settle_status 时 where 用了没索引的 channel,一分钟内 12 万行被锁,其他会话的 update 全部排队,processlist 里一片 updating。日志里能对上时间点,这种反而好排。

InnoDB 没有锁升级这回事,行锁数量不会自动变成表锁。条件是全表扫描时,扫到的每一行都要加锁,看起来就像整表被锁。锁的行数等于扫过的行数,跟最后改了几行无关。这一点先确认清楚,后面就不用反复试。

EXPLAIN UPDATE settle SET status = 2 WHERE channel = 'wx';
ALTER TABLE settle ADD INDEX idx_channel_status (channel, status);

这次卡住的地方就在「非索引更新锁范围」这一环,单独把它拎出来验证,别和其他改动混在同一次发布里。线上出问题先对时间点,跟发布、备份、任务执行的时刻排一排,能省掉一半功夫。

日志级别临时调高可以,记得改回去,长期开着既占磁盘又掩盖真问题。

处理上按这个顺序:先 explain 这条 update 确认 type 是不是 ALL;给 where 条件补索引 让扫描范围收敛到目标行;确认索引区分度够 避免优化器仍然选全表扫;改完在 data_locks 里数一遍锁行数。

结果对不对很好判断,同一条 update 的 explain 从 ALL 变成 range data_locks 里锁行数从 12 万降到几百。

作者 9823189