“秒杀”是电商平台最典型的高并发促销场景,双十一等大促活动也常以秒杀能力作为数据库技术实力的标志。随着小红书电商业务快速增长,直播带货等爆品场景对极致下单速度的需求更加突出,希望将下单吞吐提升至1W+/s。
基于MySQL内核实现的合并秒杀优化,相对之前自研的排队秒杀方案,将秒杀写入能力再提升5倍,相对MySQL社区版本,更有百倍的性能提升。设计上保持了与排队秒杀一致的兼容性,该能力对业务完全透明:仅需升级MySQL内核,无需改动SQL,即可获得显著的性能飞跃。
该方案不仅能显著提升库存、优惠券、红包等高抢购场景的用户体验,也能在热门笔记点赞等高频写入场景实现数量级的性能增强。
一、背景
2024年,小红书数据库团队首次通过热点线程排队,将自研版本的秒杀性能提升了10倍,但这依然跟不上业务的快速发展。尤其是在直播带货、热门笔记点赞、爆品抢购等场景下,业务迫切需要更快的秒杀速度。为此,数据库团队在自研内核上迭代实现了合并秒杀方案,将热点行更新速度提升5倍至1.5W/s+,极大提升了用户的使用体验。
二、收益概览
合并秒杀版本在设计时充分考虑了与排队版本的兼容性,内核会根据SQL自动选择最优的秒杀方案。对于热点SQL,内核会依据其特点自动选择合并秒杀、排队秒杀或普通更新,业务只需升级MySQL内核版本,无需修改SQL即可享受到5倍以上的性能提升。
从下面的性能分析图可以看到,在128线程秒杀时,TPS从4276提升至23543,提升约5.5倍。在1024线程的极端场景下,仍然有4.7倍的性能提升。值得注意的是,随着线程数的升高,线程切换的开销越来越大,TPS会逐步下降,因此建议将并发线程数保持在128-256之间以获得最佳性能。

测试数据均为使用sysbench模拟标准库存扣减模型进行的压测数据。
三、热点瓶颈问题分析
首先,我们对典型的秒杀场景进行分析,抽象出其核心事务模型。下面展示的是最经典的库存扣减模型:
begin;
insert into inventory_log value (...); -- 插入库存修改的流水表
update inventory set quantity=quantity-1 where sku_id=? and quantity > 0; -- 扣减库存表
commit;
随着并发数的增加,数据库的update写入性能会急剧下降,最终基本处于不可用状态(TPS约为100-200),出现严重卡顿。下面将依次分析不同方案的性能瓶颈点及相应的解决思路。
1、排队秒杀提升点
在社区原生版本中,大量并发更新同一行数据会引发激烈的锁竞争和繁重的死锁检测任务,这是主要的性能瓶颈。通过火焰图可以清晰看到,死锁检测相关函数(如DeadlockChecker::get_next_lock)消耗了大量CPU资源。

排队秒杀方案的核心思路是减少并发进入死锁检测环节的事务数量。通过引入排队机制,让请求有序地进入临界区,从而大幅降低了死锁检测的压力。如下图所示,优化后死锁检测的瓶颈消失,TPS得以提升至3K+。

2、合并秒杀提升点
再次分析上面的库存扣减模型,每个事务都是(begin; insert; update; commit)的格式。其中,insert操作针对的是不同的行(主键不同),因此可以并发执行。但update操作是对同一行数据的改写,在InnoDB层面是串行执行的,红色区域标识的便是这个行锁临界区。因此,性能的终极瓶颈就在于对同一行的update修改上,合并秒杀方案(秒杀V2)正是为了解决这个瓶颈而设计。
如下图所示,多个事务对同一行的写入在时间线上是串行的。

那么,优化思路就很明确了:既然多个事务都要修改同一行,能否将多次update的结果先在内存中进行合并,然后一次性写入InnoDB呢?这样,多次串行过程就能被合并成一次。如下图所示,该方案因此被称为“合并秒杀”。

3、方案总结
将上述方案的瓶颈点和解决思路整合,可以直观地看到各方案的演进与优化重点:
- 社区版:面临死锁检测、持有行锁、Redo日志更新、Binlog更新四个主要性能卡点。
- 排队秒杀:主要优化了“死锁检测”瓶颈,通过排队减少并发冲突。
- 合并秒杀:在排队秒杀的基础上,进一步优化了“持有行锁”、“Redo日志更新”和“Binlog更新”环节,通过合并写入大幅压缩临界区时间。



四、整体设计
合并秒杀方案的本质是将多个事务的SQL合并到一个事务中进行提交,这不可避免地需要修改MySQL的事务模型,涉及事务系统、锁系统、Binlog系统等多个核心模块。该方案设计力求实现以下优势:
- 生态组件无感知:将改动收敛到MySQL内核,输出的Binlog内容和格式与社区版完全一致,对于DTS、Canal等下游组件无感知,避免了联动升级的复杂度。
- 内核升级无感知:不修改InnoDB的存储格式,不影响版本兼容性,支持安全回退。
- 业务SQL无修改:语法与排队秒杀版本完全兼容,支持动态开关。业务或DBA可以随时将合并秒杀降级为排队秒杀,也可以同时运行多种模式,极大降低了业务迁移和试错成本。

一句话总结本方案:合并秒杀通过Leader线程预读取数据并写入缓存,Follower线程在缓存中完成数据合并扣减,最后由Leader一次性将合并结果写入存储引擎,从而极致压榨了写入性能。
1、缓存可见性与一致性
要实现合并秒杀,需要解决两个关键问题:数据在多线程间的可见性 和 Leader-Follower模式下的数据一致性。
1)数据可见性
MySQL默认的数据是线程私有的,而合并秒杀需要跨线程共享数据。为此,我们引入了按表维度的全局缓存(RowCache),所有操作同一张表的线程都访问同一份缓存。缓存的生命周期与表结构体绑定,管理起来非常方便。

2)数据一致性
解决了可见性,核心便是保证Leader和Follower线程间的数据同步一致性。下图系统展示了一个Leader和两个Follower线程如何协作完成三次扣减:
- 三个客户端发送相同的
update语句,在Queue PK阶段,由于开启了合并秒杀,会跳过排队直接进入合并逻辑。
- 三个线程竞争一个独占锁。最先抢到的线程将自己标记为Leader,然后读取InnoDB中的原始数据,完成扣减计算,并将结果写入全局缓存。随后,Leader释放独占锁,进入“收集”状态,等待一段时间以汇聚更多的Follower请求。
- 剩余两个线程竞争独占锁,抢到的标记为Follower。Follower将全局缓存中的数据读入自己的线程缓存,在本地完成扣减计算,然后将结果再次写回全局缓存。完成后释放锁,进入等待唤醒状态。
- Leader线程等待收集超时后,重新获得锁,将全局缓存中的最终结果作为本组事务的扣减值,开始执行两阶段提交(2PC)。提交完成后,Leader唤醒所有处于等待状态的Follower线程,整个事务组结束。

2、行锁极致优化
上述流程看起来已经配合得很好了,但还有优化空间吗?
让我们再次审视update操作,可以将其分为两个阶段:收集更新缓存阶段(Phase 1)和提交阶段(Phase 2)。

如果多个事务组严格按照时间串行,那么上一个组必须commit完成并释放行锁后,下一个组才能获取行锁开始执行。这样,组与组之间仍然是完全的串行。

更优的思路是流水线并行:当第一个组在进行commit(Phase 2)时,第二个组完全可以开始数据收集(Phase 1),无需等待。例如,库存从1000扣减50到950,当第一组提交950时,第二组可以从950这个值开始进行它的扣减计算。
如下图所示,绿色部分就是通过流水线并行所节省的时间,这进一步压榨了系统吞吐。

3、Binlog并行提交
在合并秒杀中,整个事务组的Binlog由Leader线程统一写入和提交。这带来了几个好处:
- Binlog由若干个组(Group)组成,每次合并提交对应一个组。
- 同一个组内的所有事务要么整体提交,要么整体回滚,保证了操作的原子性。
- 每个事务在Binlog中仍然独立记录其扣减结果(例如Leader记录扣减1,而不是合并后的扣减总值),这保证了Binlog行为与社区版完全一致。下游组件(如Canal、DTS)无需任何修改即可正常消费。

4、Crash Recovery优化
Crash Recovery过程简述:先解析Binlog生成一个待处理的事务集合,然后与Redolog进行对比,决定哪些事务需要提交,哪些需要回滚。
合并秒杀带来了新挑战:Redolog记录的是合并前后的值(如1000→900),而Binlog记录的是每个独立事务的改动(1000→999,999→998……)。因此,要正确回滚或提交Redolog的内容,必须保证整个Binlog事务组的完整性。


针对可能出现的Binlog组不完整(如最后一个事务丢失)的情况,Recovery逻辑需要特殊处理:
- 如果Binlog中某个组的Header和Tailer标记完整,则提交该组对应的Redolog。
- 如果标记不完整,则回滚该组对应的Redolog。
这样做,实际上是以数据组为单位进行恢复,粒度比单个事务更大,但保证了数据的一致性。

五、秒杀兼容性
为了让内核智能选择最优路径,我们设计了一套Hint关键字来标识SQL的意图。业务方也可以通过这些Hint来细粒度控制行为。

内核的决策流程如下图所示,是一个渐进式的兼容性设计:
- 判断特性是否开启:首先检查合并秒杀特性是否开启。
- 判断是否为单商品:使用
target_affect_row_h(1) hint,只有预期更新一行的SQL(单商品秒杀)才会进入合并流程。
- 判断能否合并:使用
queue_on_value_h和commit_on_success_h hint。因为合并时Leader会代替Follower提交,所以必须保证被合并的SQL是事务中的最后一个DML语句,否则不能合并。
- 安全退化:凡是不满足以上条件的SQL,将自动退化为排队秒杀模式。这意味着业务升级新版本后,最多没有性能提升,但绝不会出现性能下降,保障了升级的平滑和安全。

通过这种在高并发场景下对数据库内核的深度定制与优化,小红书在不扰动业务的前提下,实现了秒杀系统性能的数量级提升,为电商及其他高频写入业务提供了坚实的技术保障。这类深入内核的优化实践,正是技术团队追求极致性能的体现,值得在云栈社区这样的技术平台进行更深入的探讨与交流。