最終一致性落地實踐
http://blog.itpub.net/29254281/viewspace-1819422/
單位最近趕工,要做一款直播APP
本來關於後端,我建議了很多設計
包括
1.應用Redis鍵空間/事件通知功能,做兩級快取(Redis,JVM)
2.Redis讀寫分離.(這個很好做,但是單位大部分專案,還是沒有這麼用.主要是訪問量還是低)
3.單執行緒處理併發控制
4.最終一致性分庫設計.
5.系統基於ES彈性搜尋構建,而不是基於資料庫構建.(不涉及事務的查詢,一律走搜尋進行查詢)
6.ELK監控事務補償
最終除了資料庫最終一致性的設計,其餘都被拍了回來...
用之則行,舍之則藏
人不能逆勢而行。。。
最終一致性,無外乎用Translog和Msglog保障使用者的帳號餘額最終一致.
如果不一致則事務補償.
因為時間短,我在原來分庫的架構上,做了一些簡化.
原來的設計,是有佇列的.
我開始用全域性的TransLogID mod 3 寫日誌.
如果成功,則分為兩個Callable(傳送方增加餘額的任務和接收方扣減金額的任務)掛到執行緒池佇列中處理.
最後,更新Redis傳送方的快取.
後來發現,這個結構無法獲取傳送方真實的帳號餘額.因為有一部分應該扣減的金額,還線上程池佇列中等待處理.
當時開會討論的時候,領導們居然沒人注意到這個問題,透過了這個方案.
這個問題如果不能處理,就有點丟人現眼了.
第一版補救..原來按照TransLogID mod 3 找到資料庫,寫Translog表
那麼現在改為 傳送方ID mod 3 找到資料庫,寫Translog表.
這樣,最終處理的時候,傳送方的帳號表,Msglog表和Translog表都會落在同一個資料庫上.
如果給Translog表,增加一個狀態欄位,表明這個事務正在處理 還是 已經完成.
傳送方的扣減過程,以更新translog的狀態,作為事務完成的標誌.
這樣傳送方的事務完成標誌由translog記錄,就和msglog沒有關係了.
msglog僅僅記錄接收方的情況.最終用於比對
這樣,統計傳送方餘額的時候,我可以鎖定傳送方的帳號,和正在處理的Translog記錄,就可以得到使用者的真實餘額。
translog中未處理的記錄,就是線上程池佇列中已經扣減的金額
第二版補救
後來我分析了一下場景
檢查帳號餘額需要一個當前讀的select,鎖定使用者的帳號資訊和未處理的translog資訊.
寫translog 需要一個Insert
傳送方任務處理的時候,
一個update更新帳號餘額,記錄兩個業務日誌
update translog狀態.
這個過程,幾個行鎖,三個insert,兩個update,一個select一共6個SQL
與其這樣,不如改為同步扣減傳送方帳號,非同步處理接收方資訊.
傳送方的扣減過程如下
1.呼叫check_diamondaccount_exist
檢查鑽石帳號是否存在
是否有足夠鑽石餘額
帳號是否被鎖定
資料是否被篡改
2.如果透過第一步的檢查,扣減傳送方的鑽石帳號
3.寫入事務日誌表,呼叫insert_translog.並且提交資料庫事務
4.JAVA組成一個非同步任務,用於增加接收方的票帳號
5.更新Redis 傳送方的鑽石帳號餘額
一次當前讀,鎖定一行使用者鑽石帳號記錄
兩次Insert業務日誌
一次update,修改帳號餘額
一次Insert translog 記錄
相對於之前的設計,少了一個SQL,並且還少了記錄的鎖定,translog不用鎖定讀了.
單位最近趕工,要做一款直播APP
本來關於後端,我建議了很多設計
包括
1.應用Redis鍵空間/事件通知功能,做兩級快取(Redis,JVM)
2.Redis讀寫分離.(這個很好做,但是單位大部分專案,還是沒有這麼用.主要是訪問量還是低)
3.單執行緒處理併發控制
4.最終一致性分庫設計.
5.系統基於ES彈性搜尋構建,而不是基於資料庫構建.(不涉及事務的查詢,一律走搜尋進行查詢)
6.ELK監控事務補償
最終除了資料庫最終一致性的設計,其餘都被拍了回來...
用之則行,舍之則藏
人不能逆勢而行。。。
最終一致性,無外乎用Translog和Msglog保障使用者的帳號餘額最終一致.
如果不一致則事務補償.
- create table account_diamond(
- UserID bigint primary key comment '使用者ID',
- DiamondCount int not null default 0 comment '剩餘鑽石數量',
- State smallint not null default 1 comment '狀態.0表示帳號鎖定.1表示正常',
- CheckSum varchar(32) not null comment '使用者ID和剩餘數量的校驗',
- CreateTime timestamp not null default current_timestamp comment '建立時間',
- UpdateTime timestamp not null default current_timestamp on update current_timestamp comment '更新時間'
- ) comment '帳號鑽石表';
- create table translog(
- TransLogID bigint not null,
- Params Text not null comment '引數資訊',
- SenderUserID bigint not null comment '傳送方使用者ID',
- CreateTime timestamp not null default current_timestamp comment '建立時間',
- CreateYear year not null,
- key(CreateTime),
- primary key(TransLogID,CreateYear)
- ) comment '事務表'
- PARTITION BY RANGE(CreateYear)
- (
- PARTITION p2016 VALUES LESS THAN (year('2016-01-01')),
- PARTITION p2017 VALUES LESS THAN (year('2017-01-01')),
- PARTITION p2018 VALUES LESS THAN (year('2018-01-01')),
- PARTITION p2019 VALUES LESS THAN (year('2019-01-01')),
- PARTITION p2020 VALUES LESS THAN (year('2020-01-01'))
- );
- create table msglog(
- TransLogID bigint not null,
- Params Text not null comment '引數資訊',
- ReceiverUserID bigint not null,
- CreateTime timestamp not null default current_timestamp comment '建立時間',
- CreateYear year not null,
- primary key(TransLogID,ReceiverUserID,CreateYear)
- ) comment '訊息表'
- PARTITION BY RANGE(CreateYear)
- (
- PARTITION p2016 VALUES LESS THAN (year('2016-01-01')),
- PARTITION p2017 VALUES LESS THAN (year('2017-01-01')),
- PARTITION p2018 VALUES LESS THAN (year('2018-01-01')),
- PARTITION p2019 VALUES LESS THAN (year('2019-01-01')),
- PARTITION p2020 VALUES LESS THAN (year('2020-01-01'))
- );
因為時間短,我在原來分庫的架構上,做了一些簡化.
原來的設計,是有佇列的.
我開始用全域性的TransLogID mod 3 寫日誌.
如果成功,則分為兩個Callable(傳送方增加餘額的任務和接收方扣減金額的任務)掛到執行緒池佇列中處理.
最後,更新Redis傳送方的快取.
後來發現,這個結構無法獲取傳送方真實的帳號餘額.因為有一部分應該扣減的金額,還線上程池佇列中等待處理.
當時開會討論的時候,領導們居然沒人注意到這個問題,透過了這個方案.
這個問題如果不能處理,就有點丟人現眼了.
第一版補救..原來按照TransLogID mod 3 找到資料庫,寫Translog表
那麼現在改為 傳送方ID mod 3 找到資料庫,寫Translog表.
這樣,最終處理的時候,傳送方的帳號表,Msglog表和Translog表都會落在同一個資料庫上.
如果給Translog表,增加一個狀態欄位,表明這個事務正在處理 還是 已經完成.
傳送方的扣減過程,以更新translog的狀態,作為事務完成的標誌.
這樣傳送方的事務完成標誌由translog記錄,就和msglog沒有關係了.
msglog僅僅記錄接收方的情況.最終用於比對
這樣,統計傳送方餘額的時候,我可以鎖定傳送方的帳號,和正在處理的Translog記錄,就可以得到使用者的真實餘額。
translog中未處理的記錄,就是線上程池佇列中已經扣減的金額
第二版補救
後來我分析了一下場景
檢查帳號餘額需要一個當前讀的select,鎖定使用者的帳號資訊和未處理的translog資訊.
寫translog 需要一個Insert
傳送方任務處理的時候,
一個update更新帳號餘額,記錄兩個業務日誌
update translog狀態.
這個過程,幾個行鎖,三個insert,兩個update,一個select一共6個SQL
與其這樣,不如改為同步扣減傳送方帳號,非同步處理接收方資訊.
傳送方的扣減過程如下
1.呼叫check_diamondaccount_exist
檢查鑽石帳號是否存在
是否有足夠鑽石餘額
帳號是否被鎖定
資料是否被篡改
2.如果透過第一步的檢查,扣減傳送方的鑽石帳號
3.寫入事務日誌表,呼叫insert_translog.並且提交資料庫事務
4.JAVA組成一個非同步任務,用於增加接收方的票帳號
5.更新Redis 傳送方的鑽石帳號餘額
一次當前讀,鎖定一行使用者鑽石帳號記錄
兩次Insert業務日誌
一次update,修改帳號餘額
一次Insert translog 記錄
相對於之前的設計,少了一個SQL,並且還少了記錄的鎖定,translog不用鎖定讀了.
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-2113781/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 最終一致性落地實踐--獲取接收方的帳號餘額
- 微服務簡單實現最終一致性微服務
- SPA 的 SEO 方案對比、最終實踐
- 讀構建可擴充套件分散式系統:方法與實踐10最終一致性套件分散式
- 訊息最終一致性最易用的新架構架構
- 分散式事務方案 - 最終一致性分散式
- 微服務的最終一致性與事件流微服務事件
- 使用 Masstransit中的 Request/Response 與 Courier 功能實現最終一致性
- CnosDB如何確保多步操作的最終一致性?
- 一種介面設計方法,最終一致性方法
- rocketmq可靠一致性的mysql落地實現MQMySql
- Serverless Kubernetes 落地實踐Server
- 機器學習落地遊戲實踐簡析機器學習遊戲
- 公有云在中國實踐落地
- 向量資料庫落地實踐資料庫
- 分散式事務解決方案-RocketMQ實現可靠訊息最終一致性分散式MQ
- 分散式事務(4)---最終一致性方案之TCC分散式
- 分散式事務最終一致性的簡單案例分散式
- CAP理論以及Eventually Consistent (最終一致性)解析
- 增長黑客國內落地實踐黑客
- 優酷弱網平臺落地實踐
- EFK 配置geo-ip落地實踐
- 得物染色環境落地實踐
- 可落地的DDD程式碼實踐
- 遊戲陪玩app原始碼的可靠訊息最終一致性方案的實現遊戲APP原始碼
- 分散式事務最終一致性-CAP框架輕鬆搞定分散式框架
- MySQL半同步複製資料最終一致性驗證MySql
- 分散式事務(六)之可靠訊息最終一致性分散式
- cassandra最終一致性相關演算法資料結構演算法資料結構
- 快取一致性最佳實踐快取
- RocketMQ系列(七)事務訊息(資料庫|最終一致性)MQ資料庫
- 分散式事務(5)---最終一致性方案之可靠訊息分散式
- DevOps落地實踐,BAT系列,敏捷看板devBAT敏捷
- 對SVN的落地與實踐總結
- Kubernetes在宜信落地實踐
- Serverless 架構落地實踐及案例解析Server架構
- vivo雲原生容器探索和落地實踐
- vivo 製品管理在 CICD 落地實踐