先缓存结果再异步批量落库是保障秒杀系统稳定与数据库性能的关键策略,核心是用内存队列或消息中间件削峰聚合请求,按阈值或时间窗口批量写入,并通过幂等、唯一索引、本地redis校验等手段保障最终一致性。

高并发秒杀后不直接写库,而是先缓存结果、再异步批量落库,是保障系统稳定和数据库性能的关键策略。核心思路是:用内存队列或消息中间件削峰,聚合多条秒杀结果,按批次(如每 100 条或每 100ms)统一写入数据库。
用内存队列 + 定时/数量双触发批量提交
适合中小规模秒杀场景,不依赖外部中间件。推荐使用 ConcurrentLinkedQueue 或 Disruptor(高性能无锁队列)暂存秒杀成功记录,另起一个守护线程或 ScheduledExecutorService 按条件刷库:
- 设置阈值:累计达 50–200 条,立即批量插入(避免延迟过高)
- 设置兜底:每 50–200ms 强制检查一次,防止低峰期积压
- 每批执行前加锁(如 ReentrantLock.tryLock())避免多线程重复刷库
- 批量插入用 MyBatis 的
<foreach></foreach>或 JdbcTemplate.batchUpdate,禁用单条 executeUpdate
接入消息队列解耦并天然支持削峰
生产环境更推荐 Kafka / RocketMQ / Pulsar。秒杀服务只负责发消息(如 SeckillResult 对象),由独立消费者服务拉取、聚合、落库:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 消费者开启手动 ACK,先缓存一批消息(比如用 HashMap
> 聚合同商品订单) - 按时间窗口(如 100ms)或数量阈值(如 100 条)触发 flush
- 聚合后按
INSERT INTO ... VALUES (...),(...),...方式批量写入,必要时分表或分库路由 - 失败时记录日志 + 发送告警,支持人工补偿或死信重投
防重复与一致性保障要点
异步落库引入了最终一致性,需在关键环节做防护:
- 秒杀成功时,必须先写本地 Redis(如
SETNX seckill:order:{orderId} 1 EX 60)并返回唯一订单号,防止重复下单 - 落库前校验该订单是否已存在(SELECT COUNT(1) FROM order WHERE order_id = ?),跳过已入库记录
- 数据库订单表主键必须是业务唯一键(如 orderId),利用唯一索引拦截重复插入
- 消费端建议加幂等表(
unique_key+status),插入前先 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE
监控与降级能力不可少
异步链路变长,必须可观测、可干预:
- 暴露队列长度、平均延迟、批量大小、失败率等指标(如 Micrometer + Prometheus)
- 当内存队列积压超阈值(如 >5000 条),自动降级为“同步强刷库”或拒绝新请求(熔断)
- 消息队列消费滞后时,可临时扩容消费者实例,或启用多线程批量消费(注意事务边界)
- 提供运维接口:手动触发 flush、清空待处理缓存、重置消费位点
不复杂但容易忽略的是:批量大小要结合数据库单次 INSERT 性能压测来定,MySQL 通常 100–500 条/批较优;同时确保每批操作在事务内完成,避免部分成功部分失败。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










