一、秒杀下单基础流程
- 接收优惠券 id
- 查询优惠券信息
- 校验:秒杀时间是否合法(未开始 / 已结束直接返回失败)
- 判断库存是否充足,库存不足直接返回失败
- 扣减库存
- 创建优惠券订单,返回订单 ID
问题:查询库存、扣减库存分为两步操作,高并发多线程场景下出现超卖,属于线程安全问题。
二、悲观锁 vs 乐观锁
表格
| 悲观锁 | 乐观锁 | |
|---|---|---|
| 核心思想 | 认定并发冲突一定会发生,操作数据前先上锁,线程串行执行 | 认定冲突不一定发生,不加锁,更新的时候检测是否被别人修改过 |
| 实现例子 | synchronized、Lock、数据库行锁 |
版本号法、CAS 法 |
| 优点 | 逻辑简单,保证一定成功 | 并发性能好,没有锁阻塞 |
| 缺点 | 并发串行,性能较差 | 高并发场景更新成功率低,可能需要重试 |
1. 悲观锁
提前获取锁,同一时刻只允许一个线程操作库存,其他线程阻塞等待。 适合写冲突频繁的业务。
2. 乐观锁
不上锁,执行更新时校验数据是否被其他线程改动;如果已经被修改,则更新失败,可以选择重试或者返回报错。 两种实现方式:
- 版本号法 数据表增加
version版本字段。
- 查询:同时读出库存 + version
- 更新:
update ... set stock=stock‑1,version=version+1 where id=? and version=旧版本号 - 如果 where 条件不匹配,说明别的线程已经修改过这条数据,本次扣减失败。
- CAS 法(基于库存本身做校验) 不需要额外增加 version 字段,直接拿查询出来的库存值作为条件。
- 查询得到 stock
- 更新:
update ... set stock = stock‑1 where id=? and stock=查询出来的旧库存 - 如果条件不命中,代表库存已经被其他线程修改,扣减失败。
CAS 缺点:ABA 问题;版本号法可以规避 ABA。