MySQL 热点行更新为什么会成为瓶颈?如何优化库存和计数器场景?
简化版
热点行是大量并发请求更新同一行,例如秒杀库存、文章点赞数、账户余额。InnoDB 行锁会让同一行更新串行化,导致锁等待、吞吐下降和延迟抖动。优化思路包括减少同步更新频率、拆分热点、库存分桶、异步汇总、乐观扣减、队列串行化、缓存前置和限流。关键是不要让所有流量都打到数据库同一行。
详细版
数据库擅长保证一致性,但同一行的写冲突无法无限并发。比如 UPDATE stock SET quantity = quantity - 1 WHERE sku_id = 1 AND quantity > 0,语义正确,但所有人都抢同一个 sku_id 时,行锁会排队。
优化要先判断业务一致性要求。余额和支付强一致,不能随便异步;点赞数、浏览量可以延迟汇总;库存可以分桶或预扣。不同场景的优化方式不同。
面试中要把“热点行是锁竞争问题”讲清楚,而不是只说加索引。
完整版教学
一、热点行是什么
热点行是被大量并发访问的同一行。
读热点可以靠缓存缓解。
写热点更麻烦,因为更新需要互斥。
InnoDB 对同一行更新会加行锁。
并发越高,等待队列越长。
二、典型 SQL
UPDATE sku_stock
SET quantity = quantity - 1
WHERE sku_id = 1001
AND quantity > 0;
这条 SQL 可以保证不超卖。
但高并发下所有请求都更新同一行。
最终吞吐受单行更新能力限制。
三、常见场景
| 场景 | 一致性要求 | 优化方向 |
|---|---|---|
| 秒杀库存 | 高 | 分桶、预扣、队列、限流 |
| 点赞数 | 中低 | 缓存计数、异步落库 |
| 浏览量 | 低 | 批量汇总 |
| 账户余额 | 极高 | 严格事务、减少并发入口 |
| 排行榜计数 | 中 | Redis 聚合后周期落库 |
四、库存分桶
把一个商品库存拆成多个库存桶。
请求按随机或哈希选择桶扣减。
多个桶分散写入压力。
最终库存是多个桶之和。
缺点是实现复杂度和回收逻辑增加。
五、异步汇总
点赞、浏览、曝光这类指标不一定要实时强一致。
可以先写缓存或消息队列。
再按批次汇总到 MySQL。
这样减少单行高频更新。
但要处理丢消息、重复消费和最终一致性。
热点行优化的本质是把“单点串行写”改成“分散写、批量写或限流写”。
六、数据库侧注意点
索引要保证 UPDATE 精确定位行。
事务要尽量短。
避免在持锁期间调用外部服务。
观察锁等待和死锁日志。
热点写入要结合业务限流,不能只靠数据库硬扛。
七、误区和追问
- 误区:热点行加索引就能解决。 索引只能更快定位行,不能消除同一行更新互斥。
- 误区:库存扣减可以随便异步。 库存涉及超卖风险,要看业务能否接受预扣和补偿。
- 误区:Redis 计数就完全不用落库。 仍要考虑持久化、对账和恢复。
- 追问:余额场景能分桶吗? 通常不适合随意分桶,余额强一致要求更高。
- 追问:库存分桶有什么问题? 桶间不均、剩余库存回收、查询总量和补偿逻辑更复杂。
- 追问:如何发现热点行? 看锁等待、慢 SQL、同一主键更新频率和业务访问分布。
八、面试收束
回答时先指出它是行锁竞争。
再按业务一致性选择分桶、异步、队列或限流。
最后强调强一致场景不能为了性能破坏正确性。
九、常见误区与追问
- 误区:只记住 MySQL 热点行更新为什么会成为瓶颈?如何优化库存和计数器场景? 的结论就够了。 数据库题通常还要解释索引、事务、锁、执行计划或一致性边界,否则很容易被追问打穿。
- 误区:能查出结果就说明 SQL 或设计没问题。 还要看数据量扩大到 100 万行后是否仍能走合适索引、是否产生临时表或锁等待。
- 误区:所有场景都追求强一致。 读写分离、缓存、异步任务都可能牺牲一部分实时性,关键是说明业务是否允许。
- 追问:线上变慢时你先看什么? 先看慢 SQL、执行计划、扫描行数、锁等待和连接池,再判断是 SQL 写法、索引还是并发问题。
- 追问:如何证明这个方案可落地? 给出一个小数据例子,再补充约束、失败场景和回滚方案,避免只停留在概念层。
十、加强记忆
记 MySQL 热点行更新为什么会成为瓶颈?如何优化库存和计数器场景? 时,把它压成“语义正确、执行高效、并发安全”三件事。先说清这个知识点解决什么数据库问题,再用 1 个带数字的小例子说明数据量一大为什么会出差异,最后补上索引、锁、事务或执行计划里的易错点。这样面试官无论追问 SQL 写法、线上慢查,还是并发一致性,你都能沿着同一条线继续展开。