日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

设计一个抢红包系统

發布時間:2023/12/8 windows 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 设计一个抢红包系统 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

      • 1.需求分析
      • 2.表結構設計
      • 3.基于分布式鎖的實現
      • 4.基于樂觀鎖的實現
      • 5.基于悲觀鎖的實現
      • 6.預先分配紅包,基于樂觀鎖的實現
      • 7.基于 Redis 隊列的實現
      • 8.基于 Redis 隊列,異步入庫

1.需求分析

常見的紅包系統,由用戶指定金額、紅包總數來完成紅包的創建,然后通過某個入口將紅包下發至目標用戶,用戶看到紅包后,點擊紅包,隨機獲取紅包,最后,用戶可以查看自己搶到的紅包。整個業務流程不復雜,難點在于搶紅包這個行為可能有很高的并發。所以,系統設計的優化點主要關注在搶紅包這個行為上。

  • 發紅包:用戶設置紅包總金額、總數量
  • 搶紅包:用戶從總紅包中隨機獲得一定金額

搶紅包必須保證高可用,不然用戶會很憤怒。其次,必須保證系統數據一致性不能超發,不然搶到紅包的用戶收不到錢,用戶會很憤怒。最后一點,系統可能會有很高的并發。

2.表結構設計

紅包活動表

CREATE TABLE `t_redpack_activity` (`id` bigint(20) NOT NULL COMMENT '主鍵',`total_amount` decimal(10, 2) NOT NULL DEFAULT '0.00' COMMENT '總金額',`surplus_amount` decimal(10, 2) NOT NULL DEFAULT '0.00' COMMENT '剩余金額',`total` bigint(20) NOT NULL DEFAULT '0' COMMENT '紅包總數',`surplus_total` bigint(20) NOT NULL DEFAULT '0' COMMENT '紅包剩余總數',`user_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '用戶編號',`version` bigint(20) NOT NULL DEFAULT '0' COMMENT '版本號',PRIMARY KEY (`id`) ) ENGINE = InnoDBDEFAULT CHARSET = utf8;

紅包表

CREATE TABLE `t_redpack` (`id` bigint(20) NOT NULL COMMENT '主鍵',`activity_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '紅包活動ID',`amount` decimal(10, 2) NOT NULL DEFAULT '0.00' COMMENT '金額',`status` TINYINT(4) NOT NULL DEFAULT 0 COMMENT '紅包狀態 1可用 2不可用',`version` bigint(20) NOT NULL DEFAULT '0' COMMENT '版本號',PRIMARY KEY (`id`) ) ENGINE = InnoDBDEFAULT CHARSET = utf8;

明細表

CREATE TABLE `t_redpack_detail` (`id` bigint(20) NOT NULL COMMENT '主鍵',`amount` decimal(10, 2) NOT NULL DEFAULT '0.00' COMMENT '金額',`user_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '用戶編號',`redpack_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '紅包編號',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',PRIMARY KEY (`id`) ) ENGINE = InnoDBDEFAULT CHARSET = utf8;

活動表,就是你發了多少個紅包,并且需要維護剩余金額。明細表是用戶搶到的紅包明細。紅包表是每一個具體的紅包信息。為什么需要三個表呢?事實上如果沒有紅包表也是可以的。但我們的方案預先分配紅包需要使用一張表來記錄紅包的信息,所以設計的時候才有此表。

3.基于分布式鎖的實現


基于分布式鎖的實現最為簡單粗暴,整個搶紅包接口以activityId作為key進行加鎖,保證同一批紅包搶行為都是串行執行。分布式鎖的實現是由spring-integration-redis工程提供,核心類是RedisLockRegistry。鎖通過Redis的lua腳本實現,且實現了阻塞式本地可重入。

4.基于樂觀鎖的實現


第二種方式,為紅包活動表增加樂觀鎖版本控制,當多個線程同時更新同一活動表時,只有一個 clien 會成功。其它失敗的 client 進行循環重試,設置一個最大循環次數即可。此種方案可以實現并發情況下的處理,但是沖突很大。因為每次只有一個人會成功,其他 client 需要進行重試,即使重試也只能保證一次只有一個人成功,因此 TPS 很低。當設置的失敗重試次數小于發放的紅包數時,可能導致最后有人沒搶到紅包,實際上還有剩余紅包。

5.基于悲觀鎖的實現


由于紅包活動表增加樂觀鎖沖突很大,所以可以考慮使用使用悲觀鎖:select * from t_redpack_activity where id = #{id} for update,注意悲觀鎖必須在事務中才能使用。此時,所有的搶紅包行為變成了串行。此種情況下,悲觀鎖的效率遠大于樂觀鎖。

6.預先分配紅包,基于樂觀鎖的實現

可以看到,如果我們將樂觀鎖的維度加在紅包明細上,那么沖突又會降低。因為之前紅包明細是用戶搶到后才創建的,那么現在需要預先分配紅包,即創建紅包活動時即生成 N 個紅包,通過狀態來控制可用/不可用。這樣,當多個 client 搶紅包時,獲取該活動下所有可用的紅包明細,隨機返回其中一條然后再去更新,更新成功則代表用戶搶到了該紅包,失敗則代表出現了沖突,可以循環進行重試。如此,沖突便被降低了。

7.基于 Redis 隊列的實現


和上一個方案類似,不過,用戶發放紅包時會創建相應數量的紅包,并且加入到 Redis 隊列中。搶紅包時會將其彈出。Redis隊列很好的契合了我們的需求,每次彈出都不會出現重復的元素,用完即銷毀。缺陷:搶紅包時一旦從隊列彈出,此時系統崩潰,恢復后此隊列中的紅包明細信息已丟失,需要人工補償。

8.基于 Redis 隊列,異步入庫


這種方案的是搶到紅包后不操作數據庫,而是保存持久化信息到Redis中,然后返回成功。通過另外一個線程UserRedpackPersistConsumer,拉取持久化信息進行入庫。需要注意的是,此時的拉取動作如果使用普通的pop仍然會出現crash point的問題,所以考慮到可用性,此處使用Redis的BRPOPLPUSH操作,彈出元素后加入備份到另外一個隊列,保證此處崩潰后可以通過備份隊列自動恢復。崩潰恢復線程CrashRecoveryThread通過定時拉取備份信息,去 DB 中查證是否持久化成功,如果成功則清除此元素,否則進行補償并清除此元素。如果在操作數據庫的過程中出現異常會記錄錯誤日志redpack.persist.log,此日志使用單獨的文件和格式,方便進行補償(一般不會觸發)。

Redis則需要做高可用。

總結

以上是生活随笔為你收集整理的设计一个抢红包系统的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。