定时任务把一万个订单 id 拼进 where id in (...),单条 SQL 执行 12 秒,期间 data_locks 显示锁了 20 万行,其他订单的更新全部排队。乍看像缓存或者权限的锅,追下去往往不是。
UPDATE orders SET flag = 1 WHERE id IN (1001,1002,1003); SHOW ENGINE INNODB STATUS\G
追下去是这么回事:id in 列表太长时优化器可能放弃索引改走全表扫描,扫到的行都要加锁。就算走了索引,一万个不连续的主键也会产生一万次锁申请,持锁时间跟着 SQL 总耗时一起拉长。
「批量更新锁范围」这类改动最怕改一半,回滚脚本先准备好,再动生产。接口问题先分清是没发出去、发出去了没收到,还是收到了处理失败。
偶发问题先统计频率,一小时几次和一天一次,查法完全不同。
具体做法是,把大列表切成每批 500 个 id 循环提交;每批之间留一点间隔 让等待的会话有机会插入;explain 确认每批都走主键范围而不是全表扫;观察 data_locks 峰值是否随批大小线性下降。
验收就看一点,单次锁行数控制在千级 批量任务总耗时没有明显变长 其他业务的 update 不再排队。
