黑马点评09 秒杀功能总结

 1.整体业务流程

黑马点评09 秒杀功能总结_第1张图片

1.1 redis判断流程 (单线程)

1.首先获取订单id和用户id,调用lua脚本进行redis操作,lua内包括 对购买资格/库存充足的判断 、 扣库存下单、发送订单消息到Stream。

2.Stream组成消息队列,有异常自动放到pending-list

1.2线程流程 (多个线程轮流)

1.线程读取消息队列(read消息队列),如果能不能读到消息,就继续读;如果读到了订单消息,就解析消息内容,调用下单函数3写入数据库,然后回复确认ack;如果出现了异常,就去处理penging-list 步骤2。

2.处理penging-list,解析消息,下单,处理;如有异常,重复循环继续处理;直到处理完跳出。

3. 加Redisson分布式锁,调用创建订单数据库操作。

2.解决了哪些问题?

黑马点评09 秒杀功能总结_第2张图片

黑马点评09 秒杀功能总结_第3张图片

2.1.超卖问题-->乐观锁

通过乐观锁,用库存当做版本号,只要库存大于0,就可以允许下单(一人一单后面有措施解决)

在lua脚本内代码

2.2一人一单->维护set,存储用户id+优惠券id,判重

sismember去判断用户id和优惠券id的记录是否同时存在在集合内

集合结构保证即便用户数量很多,也能最大程度减少重复数量,同时存在判断效率也高

 

2.3避免lua脚本频繁读取

定义成静态资源,随时取用,不用反复读lua脚本

 

2.3为什么分布式锁Redisson

分布式锁能够保证在集群模式下,不同的服务器jvm上保持锁的唯一性

后续Redisson使用了hash结构,因为能够保证可重入性

Redisson内部自动解决了2.4和2.5的问题。

 

2.4(自实现的才有这问题)分布式锁的宕机死锁-->设置过期时间+保证原子性lua脚本

过期时间保证服务宕机之后锁会过期释放

lua脚本保证不会锁还没加过期时间就宕机

Redisson内部自动实现了过期时间和加锁的原子性操作(lua),所以不会分开执行,不会死锁。黑马点评09 秒杀功能总结_第4张图片

 

2.5(自实现的才有这问题)分布式锁的多线程误删问题-->判断线程标识+保证原子性lua脚本

判断锁的唯一线程标识,不是自己的不删

lua保证原子性,避免id判断和删锁之间的间隙

Redisson有看门狗机制,自动给锁续期,所以不存在锁过期导致别的线程获取锁然后误删的问题。只有服务宕机之后,看门狗机制也停止,才会锁过期,但这不是误删问题(服务阻塞但没有宕机)。

 2.6 Redisson可重入、可重试、自动续期

黑马点评09 秒杀功能总结_第5张图片

可重入:锁底层是hash结构,hash的名字是,hash的key(field)是线程名字,value是重入次数。

多获取一次锁次数加一,结束一次次数减一,只有次数为0线程才会释放锁。

黑马点评09 秒杀功能总结_第6张图片

黑马点评09 秒杀功能总结_第7张图片

可重试:发布订阅。订阅锁的消息,一旦其他线程释放了锁,就会发布一个消息通知别人来抢。有一个剩余重试时间waitTime,所有抢锁时间加起来如果超过这个阈值,就会放弃重试,如果还有剩余重试时间,就继续等发布然后抢,等发布的时间就是当前剩余重试时间。如果等不到,就不等了。黑马点评09 秒杀功能总结_第8张图片

 自动续期:看门狗,一个函数重置重试时间(默认30s),每次都从30s开始。然后递归,实现无线续期。

3.一些数据结构

3.1 redis里

3.1.1 用于一人一单的set

使用redis的set结构,包含订单id和用户id

redis.call('sismember', orderKey, userId) == 1)

3.1.2 消息队列 Stream

--3.5 发送消息到redis stream队列,xadd stream.orders * k1 v1 k2 v2......
redis.call('xadd', 'stream.orders', '*', 'userId', userId, 'voucherId', voucherId, 'id', orderId)

3.1.3库存Stock 用String计数

redis.call('get', stockKey)) <= 0)

3.2 mysql里

3.2.1 完整订单

持久化进来的订单

3.2.2优惠券和秒杀优惠券

优惠券数据

你可能感兴趣的:(java项目,java)