Scenerio:

fuctional requirements: 

  • generate valid coupons
  • users can only get one coupon per day
  • validate coupon

non-functional requirements: availability, latency, reliability, ACID

  • low latency
  • high availability

使用次数?多次重复使用还是一次性

使用时间?永久还是活动期间

减价多少?固定还是百分比

先决条件?满多少减还是直接减,要有会员或者subscription

如何拿到?注册还是直接给

 

核心流程

发券:同步or 异步发送,减价方式  

领券:谁能领,领取上限,领取方式 

用券:作用范围,计算方式,使用时间,使用次数

 

synchronous

client: make request, waiting for response, response from server

asynchronous 

client: make request, continuing working, get response from server and do something

 

商家侧

创建优惠券

发送优惠券

 

用户侧

领取优惠券

下单

使用优惠券

支付

 

Service:

coupon生成系统

client -> coupon service -> coupon db

 

coupon使用系统

              gateway

payment service   order service  coupon service  notification service

db          db        db

 

Storage:

券批次表   coupon_batch table

batch_id        INTEGER               1111

batch_name      VARCHAR               “黑五减价”

coupon_name       VARCHAR               “prime会员5元代金券“

rule_id        INTEGER      1010

total_count      INTEGER      10000

assigned_count

used_account

 

rule table

rule_id    INTEGER         1010

name     VARCHAR        "满减规则“

type     INTEGER          0 0满减 1 折扣

rule_content    BLOB

{

  threshold: 5.01 //使用门槛

  amount: 5  // 优惠金额

  use_range:3   // 使用范围,0-全场,1-商家,2-类别,3-商品

  commodity_id:10  //商品id

  is_mutex: true  //是否互斥

  received_started_at:2020-11-1 00:08:00 // 领取开始时间

  received_ended_at:2020-11-6 00:08:00 // 领取结束时间

  use_started_at:2020-11-1 00:00:00 // 使用开始时间

  use_ ended_at:2020-11-11 11:59:59 // 使用结束时间

}

coupon table

coupon_id    INTEGER    66889

user_id      INTEGER    1001

batch_id       INTEGER            1111

status      INTEGER    1       // 0-未使用,1-已使用,2-已过期,3-冻结

order_id     VARCHAR    13234242

received_time   DATETIME        //领取时间

validate_time  

used_time

 

                                  异步

商家 -发券请求->管理服务器->-发送发券消息->消息中间件-消费发券消息->服务器-coupon在用户优惠券表中插入数据->数据库

                                   | <-------插入成功-----------

                                  触达系统 -消息推送->客户端

kafka多个partition, 10w messages/100 partition = 1k  每个queue有1k message

100 consumer 

重复消费的问题?一个message分给两个consumer

 

运营提供满足条件的用户文件,上传到发券管理后台并选择要发送的优惠券。

管理服务器根据用户id,券批次id生成消息,发送到消息中间件中。

优惠券服务器消费信息

INSERT INTO coupon(user_id, coupon_id, batch_id) VALUES(1001,66889,111);

UPDATE coupon_batch set total_count = total_count - 1, assign_count = assign_count + 1

WHERE batch_id = 1111 AND total_count > 0

 

用事务:

悲观锁:select.. for update数据库自动上锁,commit/roll back时候才会被释放。perf不好

乐观锁:用version

UPDATE coupon_batch set total_count = total_count - 1, assign_count = assign_count + 1, version = version + 1

WHERE batch_id = 1111 AND total_count > 0

 

领券

商家-发布券->管理服务器-插入券批次表->数据库<-插入优惠券表-优惠券服务器<-领券请求-客户端

            <--插入成功--           --插入成功--->

 

1. 校验优惠券余量

2. 新增优惠券用户表,扣减余量

 

用户领券出现秒杀情况:

问题:高并发导致数据库崩溃,措施:缓存预热,问题:超高并发导致缓存放行量大使数据库崩溃,措施:消息队列异步处理

 

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

  1. 在领券前先查缓存 SISMEMBER batch_id:1111:user_id 1001 判断成员元素是否是集合的成员。
  2. 领券
  3. 领券后更新缓存 SAAD batch_id:1111:user_id 1001 将一个或多个成员元素加入到集合中,已经存在于集合到成员元素将被忽略。

 

用券

确认订单页,对优惠券进行校验

  1. 判断是否过期
  2. 判断使用范围
  3. 判断是否达到门槛
  4. 判断是否互斥

 客户端 ---查询券请求--->优惠券服务器------查询表----->数据库

    <-返回是否可用券-                  <-返回优惠券规则---

 

阶段    系统

确认订单  券系统

提交订单  券系统,订单系统

支付订单  券系统,订单系统,支付系统

 

同时操作多个系统,如何保障一致性?coordinator

coupon_opt_record table 优惠券操作记录表

user_id

coupon_id

operating       0-锁定,1-核销,2-解锁

operated_at      

 

TCC try-confirm-cancel分布式事务主流解决方案

try:将资源冻结。创建订单,状态冻结

confirm:确认执行业务操作,做将冻结的资源,真正扣减。订单支付成功,状态已使用。

cancel: 取消执行业务草错,取消try预留的业务资源。支付失败/超时,或者订单关闭,状态未使用。

 

posted on 2024-01-24 09:54  dddddcoke  阅读(29)  评论(0编辑  收藏  举报