分布式缓存Redis

秒杀库存控制

2024-05-22 03:47:22

业务场景

对某个套餐或产品进行销售,设置待销售品总数量。销售时不能出现超卖情况,即销售品剩余数量不能为负数。

可以分为以下几个步骤:

1. 用户请求进入系统:当用户发起请求时,请求会首先进入负载均衡服务器。

2. 负载均衡:负载均衡服务器会根据一定的算法将请求分发给后端多台服务器,以达到负载均衡的目的。

3. 业务逻辑处理:后端服务器接收到请求后,进行业务逻辑处理,并根据请求的商品数量、用户身份等信息进行校验。

4. 库存扣减:如果库存充足,后端服务器会进行库存扣减操作,并生成订单信息,返回给用户成功的信息;如果库存不足,则返回给用户失败的信息。

5. 订单处理:后端服务器会将订单信息保存到数据库中,并进行异步处理,例如发送消息通知用户订单状态。

6. 缓存更新:后端服务器会更新缓存中的商品库存信息,以便处理下一次请求。

整个过程会多次访问数据库,下单通常是利用行级锁进行访问限制,抢到锁才能查询数据库和下单。但是秒杀时的大量订单请求,往往使数据库访问阻塞。

业务需求

系统高并发,极端热门套餐抢购比率只有1%,比如100个销售品在几秒内抢购完,在前端库存判断需要支持上10w的库存快速判断

商品不能出现超售情况

多个商户进行销售,保证剩余商品数量的数据一致性

需求分析

获取商品剩余数量进行条件判断和更新数据操作,是两个独立的操作,中间有可能有其他应用修改数据从而产生冲突。在冲突的发生时,用户需要合并最新的数据,再进行条件判断和数据修改。将两个独立操作封装成一个原子的服务,保证(剩余数量-销售量)之后,商品剩余量不为负数;.

示例方案:使用LUA脚本实现

由于redis是单线程处理,修改数据不需要上锁,调用LUA脚本可视为一个原子的操作,能高效的执行和计算。参考如下:

 

local n = tonumber(ARGV[1])

if not n  or n == 0 then

    return 0

end

local vals = redis.call(\"HMGET\", KEYS[1], \"total\", \"booked\", \"remain\");

local booked = tonumber(vals[2])

local remain = tonumber(vals[3])

if booked <= remain then

    redis.call(\"HINCRBY\", KEYS[1], \"booked\", n)

    redis.call(\"HINCRBY\", KEYS[1], \"remain\", -n)

    return n;

end

return 0



NweUMkwu7Hwe