锁冲突优化的核心是“锁得更准、更短、更少碰”:优先行锁并确保索引有效,按业务分桶收缩锁范围,事务精简+批量拆分实现快进快出,隔离级别依场景选READ COMMITTED或REPEATABLE READ。

锁冲突优化的核心不是“加得更牢”,而是“锁得更准、更短、更少碰”。粒度是起点,调对了,后续很多问题自然缓解。
优先用行锁,避免无索引导致的锁升级
InnoDB 默认走行锁,但前提是 WHERE 条件能命中主键或唯一/高效二级索引。一旦没索引,MySQL 可能扫描全表,对每行加记录锁,甚至触发间隙锁扩散——实际效果接近表锁,冲突面陡增。
- 用 EXPLAIN 验证 UPDATE/DELETE 的 key 列是否非 NULL,rows 是否合理(比如预期改 1 行却显示扫描 10 万行)
- 高频更新字段(如 status、version、updated_at)尽量放在联合索引前列,确保 WHERE 能精准定位
- 避免在 WHERE 中使用函数、OR、LIKE '%xxx'、隐式类型转换等让索引失效的操作
按业务场景收缩锁作用域
锁粒度不单指“行 or 表”,更体现在逻辑范围上。同一张表里,不同业务模块操作的数据可能天然隔离——利用这点,可人为划分锁边界。
- 用户类系统可按 user_id 哈希分桶(如 mod 16),把全局账户余额锁拆成 16 个独立锁,大幅降低热点行争抢
- 订单状态流转中,将“待支付→已支付”和“已发货→已完成”拆到不同事务链路,避免长生命周期锁覆盖整条流程
- 报表统计类写入与核心交易写入分离库表,物理隔开锁资源
让锁“快进快出”,而不是“占着不放”
锁持有时间越长,其他事务等待窗口越大。粒度再细,若拖着不放,照样堵死。
- 事务内只保留真正需要原子性保障的 SQL,把日志记录、RPC 调用、格式转换等移出事务
- 批量更新必须拆分,例如 LIMIT 100 + WHERE id > last_id,每次提交后释放锁,而非一口气锁几千行
- 避免 SELECT ... FOR UPDATE 后长时间停留(如等外部接口返回),宁可先查、再算、再带条件 UPDATE(WHERE version = ?)
配合隔离级别,动态调节锁行为
粒度选择要和隔离级别联动。REPEATABLE READ 默认启用间隙锁,容易让范围查询意外锁住大片空白区间;而 READ COMMITTED 下,间隙锁仅在唯一索引冲突时触发,锁范围更收敛。
- 读多写少、允许不可重复读的业务(如商品浏览、活动参与统计),默认用 READ COMMITTED
- 强一致性场景(如资金转账)保留 REPEATABLE READ,但需配合同步机制(如幂等号+状态机)控制重试,而非依赖长事务保一致
- 绝不使用 SERIALIZABLE,除非极小流量且明确接受吞吐归零










