分散式事務和分散式hash
分散式事務是什麼?
分散式事務就是保證各個微服務之間數據一致,本質上就是保證不同資料庫的數據一致性。一致性狀態包含
- 強一致性,任何時刻,所有節點中數據都是一樣的
- 弱一致性,數據更新後,只能訪問到部分節點數據或者是全部訪問不到
- 最終一致性,不保證任何時刻一樣,但隨著時間推移最終會達到一致性狀態
因此,存在如下幾種方案:
2PC ,二階段提交是一種盡量強一致性設計,引入一個事務協調者來協調和管理各參與者的提交和回滾,包含準備和提交兩個階段,階段之間同步阻塞,準備階段協調者有超時機制。
大致流程:
- 準備階段 向各個參與者發送準備命令,可以理解為把除了提交事務之外的事情都做好。所有參與者都返回準備成功則下一階段提交事務,否則下一階段協調者就會向所有參與者發送回滾事務的請求,即分散式事務執行失敗。
- 提交階段 可能提交事務也可能回滾事務,並且存在回滾失敗或者提交失敗,失敗之後就會不斷重試。
存在問題:
- 單點故障。協調者是一個單點。解決辦法,通過選舉得到新的協調者,各個組件都記錄log
- 同步阻塞,效率低。①階段之間阻塞。②其中一個參與者佔用了共享資源就只能阻塞等待。
- 不確定性。提交階段協調者發送提交命令之後,只有一個參與者收到命令,但是兩個都掛了,新的協調者並不知道接下來是提交或者是回滾。
- 數據不一致。極端條件下數據不一致。
場景:目前支付寶使用2PC兩階段提交思想實現了分散式事務服務,它是一個分散式事務框架,用來保障在大規模分散式環境下事務的最終一致性。
3PC,為了解決2PC的不確定性,包含準備,預提交,提交三個階段。準備階段只是詢問參與者的狀態,其他階段分別對應2PC。相比2PC,參與者也有超時機制(防止在2PC協調者提交階段準備發送命令的時候掛了,參與者一直阻塞等待的情況),並且新增了一個階段使得故障恢復之後協調者的決策複雜度降低(解決2PC的不確定性,在將要發生不確定性時,新協調者發現有一個參與者處於預提交或者提交階段,那麼表明已經經過了所有參與者的確認了,所以此時執行的就是提交命令)
場景:沒有找到具體實現,偏理論。
2PC 和 3PC 都不能保證數據100%一致,因此一般都需要有定時掃描補償機制。
TCC,2PC 和 3PC 都是資料庫層面的,而 TCC 是業務層面的分散式事務。TCC指的是Try - Confirm - Cancel
- try,即資源的預留和鎖定。
- Confirm,指的是確認操作,其實就是真正的執行。
- Cancel,指的時撤銷操作,可以理解為撤銷預留階段的動作。
其也存在一個事務管理者,用來記錄TCC全局事務狀態提交或者回滾。其流程和2PC差不多。但業務的侵入較大、業務耦合度高,需要將原來一個介面可以實現的邏輯拆分為三個介面。
場景:TCC 需要提供三個介面,提高了編程的複雜性,並且依賴於業務方來配合提供這樣的介面,推行難度大,所以一般不推薦使用這種方式。
本地消息表,利用各個系統本地事務來實現分散式事務。系統中會定義一個存放本地消息的表,一般都是放在資料庫中。
大致流程:
- 當A被其他系統調用需要業務執行時,將業務的執行操作和將消息放入本地消息表中的操作放在同一個事務中。
- A定時輪詢本地消息表往mq中生產消息,失敗則重試。
- B消費mq中的消息,並處理業務邏輯,如果本地事務失敗則重試,如果是業務失敗則通知A進行回滾。
場景:跨行轉賬可通過該方案實現。在銀行一的用戶A向銀行二的用戶B轉賬
- 銀行一:在一個本地事務中扣掉A的錢並將轉賬消息寫入本地消息表中,如果本地事務失敗則失敗,如果本地事務成功,系統定時輪詢消息表並往mq中生產轉賬消息,失敗則重試。
- 銀行二:mq 消息會被銀行二消費並往 B 的賬戶增加轉賬金額,執行失敗會不斷重試。
消息事務,只有阿里的RocketMQ支援,實現了最終一致性。
大致流程:
- A向mq發送準備消息,失敗則直接取消,成功則執行本地事務。
- 本地事務執行成功,向mq發送確認消息,失敗則回滾消息。
- B定期消費mq中的確認消息,執行本地事務並回送ack消息,如果本地事務執行失敗,會不斷嘗試,如果是業務失敗,會向A發起回滾請求。
- mq會定期輪詢所有準備消息,調用A提供的反查事務狀態介面,如果該準備消息本地事務執行成功則重發確認消息,否者直接回滾。
場景:用戶註冊成功後發送郵件、電商系統給用戶發送優惠券等需要保證最終一致性的場景。
最大努力通知,是最簡單的一種柔性事務,適用於一些對最終一致性不敏感的業務,且被動方的處理結果,並不會影響主動方的處理結果。
大致流程:
- A本地事務執行完成之後,向MQ生產消息。
- 會存在一個服務消費MQ消息並調用系統B的介面。
- 如果B執行成功則OK,否則會一直嘗試N次,超過則放棄。
場景:最常見的場景就是支付回調,支付服務收到第三方服務支付成功通知後,先更新自己庫中訂單支付狀態,然後同步通知訂單服務支付成功。如果此次同步通知失敗,會通過非同步腳步不斷重試地調用訂單服務的介面。
分散式Hash是什麼?
我們從分散式系統中負載均衡的問題來描述分散式hash。
常見的負載均衡演算法如下:
隨機訪問策略。隨機訪問,可能造成伺服器負載壓力不均衡。
輪詢策略。請求均勻分配,但是浪費了性能高的伺服器的資源。
權重輪詢策略。根據權重輪詢,權重需要靜態配置,無法自動調節。
Hash取模策略。通過hash取模,伸縮性差,當新增或者下線伺服器機器時候,用戶與伺服器的映射關係會大量失效。
一致性哈希策略。簡單來說就是將整個哈希值(int範圍)空間組織成一個虛擬的hash圓環,將每個伺服器標識符跟int最大hash取模,得到一些對應在hash環上的點。用戶在訪問的時候,根據用戶的標識符使用同樣的hash函數取模,得到hash環上的一點,但這一點很可能沒有伺服器映射在上面,所以會順時針行走,遇到的第一台伺服器就是應該處理該用戶請求的伺服器。
優點:
- 可以任意動態添加、刪除節點,每次添加、刪除一個節點僅影響hash環上相鄰的節點。
缺點:
- 會存在數據傾斜問題,因為hash值範圍很大(int範圍),用戶請求量也很大(hash取模分布相對均勻),而伺服器數量相對很少(hash取模分布很不均勻),就會造成數據傾斜問題。解決辦法就是設置虛擬伺服器,每個真實伺服器映射很多個虛擬伺服器,這樣伺服器數據大幅增加,hash取模分布相對均勻。