方案设计
具体设计
缓存
选用Redis
缓存穿透解决方案:
当查询时缓存未命中,从数据库load数据到缓存,如果红包不存在,则load一条数据(key为红包id,value标记红包不存在)到缓存中。
缓存击穿解决方案:
加互斥锁,利用redisTemplate.opsForValue().setIfAbsent()方法
循环:
查缓存
缓存为空,则尝试加锁,否则跳出循环
尝试加锁失败,则跳到第1步继续循环,尝试加锁成功,则从DB load数据到缓存
// TODO
1.存储格式优化,改用hash格式
缓存雪崩解决方案:
消息队列
mq选型:RocketMq
具体方案:
先从缓存中查红包剩余数量,如果数量为0则直接返回,如果不为0,则使用mq发送一条红包金额扣减消息,然后返回秒杀中,客户端轮询秒杀结果。数据库层订阅扣减消息,进行红包金额扣减,扣减成功后刷新缓存。
前端/客户端
点击抢
后,弹出加载动画,此时后端一般返回正在抢中的状态,然后前端轮询抢红包状态,直到查到抢红包结果确定,结束动画,展示结果。
扣减红包金额
金额全部使用int型,单位为分。
不可重复读:从查询红包余额时加排它锁,防止查询余额后,有其他线程更新了该余额,导致不可重复读。
解决办法:
排他锁(for update):mybatis实现排他锁不够优雅,抛弃
分布式锁
分布式锁:
使用stringRedisTemplate.opsForValue().setIfAbsent()方法实现
锁设置超时时间,如果线程卡住了,锁过期后其他线程可以抢占锁。
// TODO
当单次处理时间过长时,锁过期,存在锁泄漏情况。
解决方案:做好监控。
接口限流
使用guava的RateLimter配合aop实现
数据库设计
数据库:red-envelope
数据库表:
1.envelope_detail(红包详情)
2.envelope_grabber(抢到红包的人和金额等)
常见问题
1.如何解决抢超问题()
在sql加上判断防止数据边为负数
数据库加唯一索引防止用户重复抢红包
redis预减库存减少数据库访问 内存标记减少redis访问 请求先入队列缓冲,异步下单,增强用户体验
对账
压力测试
Jmeter/Siege
可优化点
数据库分库分表
redis集群宕机解决方案
应用监控、基础设施监控
防范脚本抢红包:负载均衡设备中做一些配置,判断如果同一个用户在1分钟之内多次发送请求来进行抢红包,就认为是恶意脚本代抢
利用负载均衡层来过滤无效请求(红包已抢完)
红包过期,多余金额退换
set化:红包系统根据红包 ID,按一定的规则(如按 ID 尾号取模等),垂直上下切分。切分后,一个垂直链条上的逻辑 Server 服务器、DB 统称为一个 SET。各个 SET 之间相互独立,互相解耦。并且同一个红包 ID 的所有请求,包括发红包、抢红包、拆红包、查详情详情等,垂直 stick 到同一个 SET 内处理,高度内聚。通过这样的方式,系统将所有红包请求这个巨大的洪流分散为多股小流,互不影响,分而治之
大key优化
参考资料
https://www.zhihu.com/question/22625187/answer/85530416 https://rocketmq.apache.org/docs/quick-start/ https://www.bilibili.com/video/av19582887/ https://www.infoq.cn/article/2017hongbao-weixin https://www.ibm.com/developerworks/cn/web/wa-design-small-and-good-kill-system/index.html https://zhuanlan.zhihu.com/p/25368538 https://www.zhihu.com/question/54895548 https://www.infoq.cn/article/2017hongbao-weixin
Last updated