大厂的优惠券系统是如何设计的?(二)
千w级用户数
这就有【非活跃用户】的问题,假设注册用户一千万,根据二八原则,其中活跃用户占20%。若采用上面拆成两个表的情况,发一封“站内信”,得执行一千万个插入操作。可能剩下80%用户基本都不会再登录,其实只需对其中20%用户插入数据。
信息表 message:
用户侧操作
登录后,首先查询 message_content 中的那些没有在 message 中有记录的数据,表示是未读的站内信。在查阅站内信的内容时,再将相关的记录插入 message。
系统侧操作
发站内信时:
- 只在 message_content 插入站内信的主体内容
- message 不插入记录
给 10W 用户发券
有什么问题?重复消费,导致超发!
- 运营提供满足条件的用户文件,上传到发券管理后台并选择要发送的优惠券
- 管理服务器根据【用户ID】、【券批次ID】生成消息,发送到MQ
- 优惠券服务器消费消息
3.4 领券
步骤
1.校验优惠券余量
2.新增优惠券用户表,扣减余量
用户领券过程中,其实也会出现类似秒杀场景。秒杀场景下会有哪些问题,如何解决?
用户重复领取或多领
Redis 数据校验!
1.领券前,先查缓存
2.领券
3.领券后,更新缓存
3.5 用券
何时校验优惠券使用规则?
- 确认订单(√)
- 提交订单
- 立即付款
确认订单页,对优惠券进行校验:
- 判断是否过期
- 判断适用范围
- 判断是否达到门槛
- 判断是否互斥
返回可用券
选择可用券,并返回结果
同时操作多个服务,如何保证一致性?
表设计
优惠券操作记录表 Coupon_opt_record
TCC,Try-Confirm-Cancel,目前分布式事务主流解决方案。
1.阶段一:Try
对资源进行冻结,预留业务资源
创建订单时,将优惠券状态改为 “冻结”
2.阶段二:Confirm
确认执行业务操作,做真正提交,将第一步Try中冻结的资源,真正扣减
订单支付成功,将优惠券状态改为 “已使用”
3.阶段三:Cancel
取消执行业务操作,取消Try阶段预留的业务资源
支付失败/超时或订单关闭情况,将优惠券状态改为 “未使用”
4 Scale 扩展
4.1 快过期券提醒
定时扫券表
缺点:扫描数据量太大,随着历史数据越来越多,会影响线上主业务,最终导致慢SQL。
延时消息
缺点:有些券的有效时间太长了(30天)以上,有可能造成大量 MQ 积压
新增通知表
优点:扫描的数据量小,效率高。删除无用的已通知的数据记录
通知信息表(notify_msg)设计
过期券提
- 在创建优惠券的时候就将需要提醒的记录插入提醒表中notify_msg
- 把用户ID+批次ID+通知日期作为唯一索引,防止同一个批次有重复的记录通知,保证每天只会被通知一次
- 建立notify_time,通知时间索引,每日的通知扫描通过该索引列查询,通过索引列来提高查询效率
- 通知完成后该表中的数据变失去了意义,通过定时任务将该数据删除
4.2 数据库层面优化 - 索引
4.3 发券接口,限流保护
前端限流
点击一次后,按钮短时间内置灰
后端限流
部分请求直接跳转到【繁忙页】
文章转自公众号: JavaEdge
感谢大神指点,给跪了
言简意赅,该说的都说了,相当受益