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
- 在領券前先查緩存 SISMEMBER batch_id:1111:user_id 1001 判斷成員元素是否是集合的成員。
- 領券
- 領券后更新緩存 SAAD batch_id:1111:user_id 1001 將一個或多個成員元素加入到集合中,已經存在于集合到成員元素將被忽略。
用券
確認訂單頁,對優惠券進行校驗
- 判斷是否過期
- 判斷使用范圍
- 判斷是否達到門檻
- 判斷是否互斥
客戶端 ---查詢券請求--->優惠券服務器------查詢表----->數據庫
<-返回是否可用券- <-返回優惠券規則---
階段 系統
確認訂單 券系統
提交訂單 券系統,訂單系統
支付訂單 券系統,訂單系統,支付系統
同時操作多個系統,如何保障一致性?coordinator
coupon_opt_record table 優惠券操作記錄表
user_id
coupon_id
operating 0-鎖定,1-核銷,2-解鎖
operated_at
TCC try-confirm-cancel分布式事務主流解決方案
try:將資源凍結。創建訂單,狀態凍結
confirm:確認執行業務操作,做將凍結的資源,真正扣減。訂單支付成功,狀態已使用。
cancel: 取消執行業務草錯,取消try預留的業務資源。支付失敗/超時,或者訂單關閉,狀態未使用。
浙公網安備 33010602011771號