优惠券系统设计

12,915 阅读5分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路 持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天.

关键字: Message Queue· 分布式事务 表单设计 Touch System 优惠券 消息队列 索引 SQL Table Design 存预热 过期券通知 触达系统

Scenario 场景

  1. 设计一个优惠券系统
  2. Coupon System
  3. 优惠券的种类
  4. 优惠券系统的核心流程

电商系统促销手段

  1. 优惠券
  2. 拼团
  3. 砍价
  4. 老带新

优惠券核心流程

发券

发券的方式:同步发送 异步发送

领券(有一些系统是没有用户主动领取的方式的)

谁能领:所有用户 领取上限:一个优惠券最多能领取多少张? 领取方式:用户主动领取 or 自动发放被动领取

注意:(有一些系统是没有用户主动领取的方式的)

用券

作用范围:商品、商户、类目 计算方式:是否互斥、是否达到门槛等

需求拆解

商家侧 1.创建优惠券- 这里还可以引申 票券模板,票券规则 2.发送优惠券

用户侧 1.领取优惠券 2.下单 3.使用优惠券 4.支付

难点:

  1. 券的分布式事务,使用券的过程会出现的分布式问题分析
  2. 如何防止超发
  3. 如何大批量给用户发圈
  4. 如何限制券的使用条件 -- 这里考验代码设计能力
  5. 如何防止用户重复领券

表单设计

1.券批次(券模板) 指一批优惠券的抽象、模板,包含优惠券的大部分属性。

例:商家侧创建了一批优惠券,共1000张,使用时间为202011-11 00:00:00 ~ 2020-11-11 23:59:59,规定只有化妆品类目商品才能使用,满100减50。

2.券 (注意:虚拟券绑定用户,实体券没有,还有,转赠票券) 发放到用户的一个实体,已与用户绑定。 例:将某批次的优惠券中的一张发送给某个用户,此时优惠券属于用户。

3.规则 优惠券的使用有规则和条件限制,比如满100减50券,需要达到门槛金额 100元才能使用。

image.png

如何给大量用户发券?

异步发送

优惠券系统 — 触达系统

触达系统:短信,邮件,站内信,公众号,微信卡包

如果系统的用户数量增加到上万人呢?

这样发一条站内信,就要重复插入上万条数据。而且这上万条数据的 content 是一样的,假设一条站内信占 100 个字节,发一次站内信就要消耗十几 MB。因此我们可以将原来的表拆成两个表。

信息表 message Table 信息内容表 message_content Table

如果系统的用户数量增加到上K万人呢?

• 这里会有非活跃用户的问题,假设注册用户一千万,其中活 跃用户只占其中的20%。

• 如果采用上面拆分成两个表的情况,发一封“站内信”,那 得执行一千万个插入操作。可能剩下的80%用户基本都不会 再登录了,我们只需要对其中20%的用户插入数据即可。

系统侧

发站内信的时候,只在 message_content 插入站内信的主体内容,message 里不插入记录。

用户侧

用户登录后,首先查询 message_content 中的那些没有在 message 中有记录的数据,表示是未读的站内信。在查阅站内信的内容时,再将相关的记录插入到 message 中。

问题 高并发导致数据库崩溃

措施:缓存预热

问题 超高并发导致缓存放行量大使数据库崩溃

措施:消息队列异步处理

如何防止用户重复领取或多领?

  1. 在领券前先查缓存
  2. 领券后更新缓存
  3. 领券

Redis数据校验 语法:SADD KEY VALUE1......VALUEN 作用:将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略。 实例:SADD batch_id:1111:user_id 1001

TCC是Try-Confirm-Cancel的简称,是目前分布式事务主流解决方案。

TCC 实现阶段一:Try 对资源进行冻结,预留业务资源 在创建订单时,将优惠券状态改为 “冻结”

TCC 实现阶段二:Confirm 确认执行业务操作,做真正的提交,将第一步Try中冻结的资源,真正扣减 在订单支付成功,将优惠券状态改为 “已使用”

TCC 实现阶段三:Cancel 取消执行业务操作,取消Try阶段预留的业务资源 在支付失败/超时,或者订单关闭情况,将优惠券状态改为 “未使用”

Scale 扩展

快过期券提醒 数据库层面优化 发券接口限流保护

快过期券提醒

定时 扫 描 券 表 缺点:扫描的数据量太大,随着历史数据越来 越多,会影响线上主业务,会出现慢 SQL。

延 时 消 息 缺点:有些券的有效时间太长了(30天)以上,有可能造成大量 MQ 积压

新 增 通知 表 优点:扫描的数据量小,效率高 删除无用的已通知的数据记录

通知信息表 Notify_msg Table 1.在创建优惠券的时候就将需要提醒的记录插入提醒表中notify_msg 2.把用户ID+批次ID+通知日期 作为一个唯一索引,防止同一个批次有重复的记录通知,保证每天只会被通知一次 3.建立notify_time,通知时间索引,每日的通知扫描通过该索引列查询,通过索引列来提高查询的效率 4.通知完成后该表中的数据变失去了意义,通过定时任务将该数据删除