EXPLAIN SELECT * FROM stock WHERE sku_code = 'A1001' FOR UPDATE; ALTER TABLE stock ADD UNIQUE INDEX uk_sku (sku_code);
扣减库存前用 select ... for update 按 sku_code 查,sku_code 上没有索引,一条语句锁了 8 万行,其他 sku 的下单全部卡住。先别急着动参数,把报错原文和时间点记下来,顺序反了容易白折腾。
原因不复杂,for update 加的锁跟着扫描路径走,没有索引就是全表扫描,扫到的每一行都加排他锁。行数多的时候锁的范围比想的要大得多,别的业务碰到同一张表就会排队。
这次卡住的地方就在「for update 锁范围」这一环,单独把它拎出来验证,别和其他改动混在同一次发布里。改完隔一天再看一次,当场正常不代表能稳住。
测试环境和线上的差别,往往就在版本号和那几行被忽略的配置上。线上出问题先对时间点,跟发布、备份、任务执行的时刻排一排,能省掉一半功夫。
处理上按这个顺序:explain 这条 select 确认访问类型;给查询条件补唯一索引 让 for update 只锁一行;确认事务里没有其他语句扩大锁范围;用 data_locks 核对锁行数是否等于 1。
改完只确认一件事:for update 只锁住目标那一行 data_locks 里记录数变成 1 不同 sku 的下单互不影响。
