iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 6
2
Software Development

服務開發雜談系列 第 6

微服務瞎談(6) BASE理論 & Transactional Outbox Pattern

BASE原則

CAP只侷限於原子讀寫的NoSQL場景下, 且也無法支援資料庫的事務交易這類的場景.
所以在資料庫如果分片分表或是多個實例節點架構上, CAP並不適用.
有些事務交易模型雖能保證分散式架構下的ACID特性(XA事務, 2PC), 但吞吐量下降很多.
對於有些產業需要高併發+低回應時間是難以接受的.

2008年, eBay公司選擇把資料庫事務的ACID原則放寬, 於ACM提出了一套BASE原則(Basically Available, Soft-state, Eventually Consistent).
並且給出它們在分散式系統中實踐這套理論的解決方案.

在之前的文章中, 會發現如果要支援高併發+低回應時間, C+P模型要寫入非常多節點.
所以BASE理論的核心思想就是即使無法作到Strong Consistency, 但可以用適當的方式達成Eventually Consistency與Availability. 可算是CAP理論的延伸.

BASE:

  1. Basically Available
    舉例, MySQL採用了partitioned分片模式, 100萬個用戶資料分在5個實例上. 但其中一個實例損毀, 其系統可用性還有80%, 所以系統是"基本可用", 至少還有80%用戶可正常登入使用.

  2. Soft-state
    這個很抽象XD
    在Client與Server的交互過程中, Server端會暫存Client的狀態資料, 但是僅僅就維持一小段時間(暫存的中間狀態), 過了這時間, 狀態就會轉成terminate或是最終狀態.
    也就是說這中間狀態, 允許系統在多個不同節點間存在資料同步延時(可以有一段時間的不同步), 但這不可影響可用性.
    像是某件事情的處理進入了retry狀態, 此時就會由client來主動刷新該狀態, 要是一段時間沒來刷新, 又或者是刷新成功, 就會轉成另一個狀態, 並且儲存同步給所有節點.

    有Soft-State, 就有Hard-State, 同等於Strong Consistency, 要求所有數據副本的該資料狀態都是一致的.

  3. Eventually Consistent
    不保證任意時刻下任意節點的同一份資料都是相同的數據.
    但是隨著時間, 不同節點同一份資料總是朝著相同的方向變化.
    也就是說在一段時間後, 副本間的資料最終會達到一致的狀態.

    Eventual Consistency

所以對於RDBMS(關聯式資料庫), BASE理論主要實現
如在Basically Available的舉例

  1. 對業務/功能資料進行垂直拆分
  2. Sharding分片(Mycat, Atlas, kingshard, Vitess...)

至於NoSQL幾乎就在BASE基礎上, 出了很多系統.
Amazone Dynamo DB, Mongo...etc.

分散式事務

eBay Transactional Outbox Pattern

eBay提出一個分散式事務解決方案, 就是透過message queue來輔助實現事務控制流程, 或者啟用排程服務來檢查是否有未投遞的資料, 來達成Eventually Consistent.

支付交易場景, 如果user購買了某商品, 需要在order表之中增加紀錄, 也要修改商品庫存.
由於這兩張表屬於不同的資料庫, 就涉及分散式事務與資料一致性問題.

核心概念是把大事務轉變成小事務.

eBay BASE use case

  1. 分散式事務發起方開啟一個本地事務, 然後進行業務操作, 這裡就是建立訂單, 並把各項資訊寫到message表(本地消息表), 成功就Commit, 往下一步走; 發生錯誤則Rollback, 回絕請求.

這裡的trans message表(本地消息表), 就是用來紀錄分散式事務的內容與狀態.

START TRANSACTION;
INSERT INTO `order` (`order_id`,`user_id`,`tx_id`,`tx_time`,`product_id`,`amount`,`price`) VALUES(123,456,9999,'2020-01-01 01:01:01',1000,1,100);
INSERT INTO `trans_message` (`tx_id`,'2020-01-01 01:01:01',`tx_time`,`status`,`user_id`,`product_id`,`amount`) VALUES(9999, `in progressing`, 1000, 1);
COMMIT;
  1. 分散式事務發起方透過MQ, 發送一則事務代處理訊息給分散式事務接收方
  2. 分散式事務接收方透過MQ收到訊息
  3. 開啟一個本地事務, 來處理扣商品庫存的事務, 完成後傳Ack給MQ
  4. 分散式事務接收方透過MQ, 發送一則事務完成訊息給分散式事務發起方
  5. 分散式事務發起方透過MQ收到訊息
  6. 分散式事務發起方開啟本地事務, 更新trans message表(本地消息表), 並且傳Ack給MQ

也能不用Message Queue, 而是排程檢查本地消息表, 把還未投遞出去的資料給投遞出去.
分散式事務接收方再打API來更新狀態.

這樣當用戶送出訂單時, API返回的會是處理中(in progressing), 過一下子就會轉成訂單成功建立.

pros

  1. 事務訊息都持久化到資料庫中, 由於AICD事務屬性保證了持久性
  2. 當一個事務訊息投遞過程中遺失了, 或是某處理節點crash, 還是能從本地消息表中查出來, 並且恢復繼續執行

cons

  1. 如果引入了MQ, 代碼上會變得較複雜; MQ也是需要考慮設計成叢集滿足高可用性
  2. 整個事務從開始到完成會有延遲
  3. 需要MQ

但這方案有幾個要留意的地方

  1. 如果推送MQ失敗, 雖然訂單還是處理中, 不會造成問題, 但如果retry失敗太多次, 也要能告警處理; 而如果是分散式事務接收方的完成事務的訊息推送失敗, 還是要仰賴本地消息表, 或者告警人為介入
  2. 因為MQ的At least once(確保訊息最少會被投遞一次), 或者排程定期掃描未完成的任務來重新發送, 所以需要確保冪等性
  3. 用MQ比用排程掃描, 會相對很即時; 且相對服務間就解耦合了
  4. 這種流程, 只適用於執行週期長, 且對實時性要求不高的場景, 像是串接第三方支付;如果用排程掃描, 不可能每秒都給掃描的,太浪費資料庫資源

上一篇
微服務瞎談(5) CAP理論
下一篇
微服務瞎談(7) Saga Pattern
系列文
服務開發雜談33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言