高并发系统库存超卖的解决之道:从数据库锁到Redis分布式锁
当你的秒杀活动上线瞬间涌入10万请求,却发现库存-100件时,就知道遇到了高并发经典难题——超卖问题。这种"库存穿透"现象不仅损失订单,更会引发资损风险。本文将解析超卖本质,并分享一线实战解决方案。
一、超卖是如何发生的?
假设商品库存=100,当两个并发线程同时读取库存为100:
- 线程A:读取库存100 → 扣减1 → 写入99
- 线程B:读取库存100 → 扣减1 → 写入99
最终库存变成99而非98,这就是典型的并发读写导致的数据不一致。在高并发场景下,每秒数千次请求会放大该问题。
二、4种主流解决方案对比
1. 数据库悲观锁(行级锁)
SELECT * FROM products WHERE id=1 FOR UPDATE; UPDATE products SET stock=stock-1 WHERE id=1;
优点:简单直接
缺点:DB连接耗尽,5000QPS下连接池瞬间崩溃
2. 乐观锁(版本号控制)
UPDATE products SET stock=stock-1, version=version+1 WHERE id=1 AND version=#{current_version};
适用场景:冲突率低的场景
实战案例:某电商大促期间通过版本号降低超卖率87%
3. 分布式锁(Redis实现)
// 加锁 Boolean locked = redis.setnx("lock:product_1", "1"); if(locked) { try { // 扣减库存 } finally { redis.del("lock:product_1"); } }
关键点:必须设置过期时间,避免死锁
2023最佳实践:Redlock算法 + Lua脚本保证原子性
4. 令牌桶限流(Sentinel/Nginx)
- 在网关层限制每秒进入订单系统的请求
- 阿里双十一实践:漏斗式流量分层过滤
三、综合解决方案建议
根据百万级QPS系统经验,推荐组合方案:
- 前端限流:按钮置灰+验证码
- 网关拦截:Sentinel配置每秒最大通过量
- 核心交易:Redis分布式锁 + Lua原子操作
- 数据兜底:定时对账任务修复差异
最新动态:2023年阿里开源RedisShake工具,实现跨机房库存同步,延迟<5ms。
结语
超卖本质是并发场景下的数据一致性问题。没有银弹方案,需根据业务特点组合使用锁机制、限流策略和异步处理。建议在预发布环境使用JMeter模拟万人并发压测,毕竟线上崩溃的代价远高于测试成本。记住:任何未经验证的"高性能方案"都是埋雷行为!
评论