Redis防止订单超卖
背景
想象一下,你开了一家超火爆的奶茶店,每天限量 100 杯招牌奶茶。顾客们都在线疯狂抢购。
核心问题就是:如何确保这 100 杯奶茶不会被第 101 个顾客抢走? 简单地在数据库里减库存 stock = stock - 1是绝对不行的,高并发下瞬间就超卖了!
解析
核心目的就是要解决在高并发下单的场景下,保证商品库存扣减的原子性和一致性
Redis是基于内存的,读写速度快,能抗住高并发。Redis可以使用Lua脚本实现CAS语义,可以将判断库存和扣减库存绑定为一个原子操作,确保在操作执行的时候不会出错。
实现
- 首先编写一个lua脚本
local stock_key = KEYS[1] local quantity_to_buy = tonumber(ARGV[1])
local current_stock = tonumber(redis.call('GET', stock_key)) if current_stock == nil then current_stock = 0 end
if current_stock < quantity_to_buy then return -1 end
redis.call('DECRBY', stock_key, quantity_to_buy)
return tonumber(redis.call('GET', stock_key))
|
- 加载和执行Lua脚本
@Component public class StockService {
@Autowired private RedisTemplate<String, Object> redisTemplate;
private static final DefaultRedisScript<Long> DEDUCT_STOCK_SCRIPT;
static { DEDUCT_STOCK_SCRIPT = new DefaultRedisScript<>();
DEDUCT_STOCK_SCRIPT.setLocation(new ClassPathResource("lua/deduct_stock.lua"));
DEDUCT_STOCK_SCRIPT.setResultType(Long.class); }
public String deductStock(String productId, int quantityToBuy) { String stockKey = "stock:product_" + productId;
Long result = redisTemplate.execute( DEDUCT_STOCK_SCRIPT, Collections.singletonList(stockKey), quantityToBuy );
if (result == null) { return "系统错误"; } else if (result == -1) { return "库存不足"; } else { return "扣减成功,剩余库存: " + result; } } }
|