傳統的單體應用不會橫跨多個數據庫,能夠經過單機事務保證一致性。然而在海量數據的場景下,我須要對數據庫作拆分,即分庫分表,而Cobar
、MyCat
這類分庫分表中間並不提供分佈式事務的特性,而且基於二階段提交的分佈式事務性能較差,對於大多數業務場景來講,並不須要強一致,只須要保證最終一致性便可。數據庫
下面咱們舉個下訂單的場景,總共有3個實體,商品
、用戶
、訂單
,咱們按照user_id
來sharding。因此相同user_id
的用戶
和訂單
在同一個物理庫下,而商品
表中不存在user_id
,因此商品
表在不一樣的物理庫下。segmentfault
下訂單的場景,主要涉及到兩個事務操做,扣減庫存
和生成訂單
,由於兩個操做涉及不一樣的數據庫,因此沒法保證強一致性。網絡
咱們能夠經過本地消息表,來實現最終一致性,具體流程以下圖:分佈式
trans_id
。商品ID
、交易流水號
、凍結數
、消息狀態
這四個字段構成,由於消息表和商品表在同一個物理庫下,因此TX1
中的操做1和操做2是能夠構成事務操做的。凍結記錄的狀態有三種:已凍結
、釋放已售出
、釋放未售出
。凍結記錄的初始狀態爲已凍結
。trans_id
生成訂單,訂單的狀態有三種:未支付
、已支付
、超時
,訂單的初始狀態爲未支付
。未支付
,開啓事務SELECT FOR UPDATE
鎖住該訂單,即用悲觀鎖阻止用戶對訂單進行支付等操做,而後經過訂單的trans_id
去凍結表更新對應凍結記錄的狀態,置爲釋放未售出
,並回滾商品數量,回滾商品的操做完成後,將訂單狀態置爲超時
,若事務中調用的回滾商品數量的服務失敗,則能夠發出報警人工處理,或經過更長時間的定時任務去處理;若訂單爲已支付
,則將凍結表中記錄的狀態置爲釋放已售出
。事務一
成功,而事務二
的訂單確實沒有建立成功的狀況,這樣會凍結一部分商品的數量,因此能夠撈取出 建立超過10分鐘 狀態爲已凍結
的全部凍結記錄,根據每一個凍結記錄的trans_id
去訂單表查詢,若不存在對應的訂單,則將凍結記錄的狀態更新爲釋放未售出
,並回滾商品數量。未支付
的訂單,會先回滾凍結表,而後將訂單狀態置爲超時
,但這最後一步將訂單置爲超時
可能會失敗,這樣會出現不一致的狀態,即訂單狀態爲未支付
,而凍結記錄的狀態爲釋放未售出
。因此,在支付的時候須要作一個前置校驗,檢查凍結記錄的狀態是否爲已凍結
,若不是,則拒絕支付。在上面這種模型的基礎上,還有一種變種,以下圖:性能
即在TX2
失敗的狀況下,跳轉到TX3
。spa
trans_id
查詢訂單,若訂單不存在,則直接將凍結記錄置爲釋放未售出
,並回滾庫存;若訂單存在,則說明TX2
由於網絡抖動等緣由而失敗,其實訂單建立成功,則進行正常的支付流程。trans_id
查詢訂單的時候,必定要開啓事務
,這樣纔會強制走主庫,若不開啓事務,則會走備庫,由於MySQL
主從同步延遲的問題,備庫極可能沒法查詢到訂單,從而回滾庫存,這顯然是錯誤的。將定時任務的壓力均勻地分配到每一次調用中,提升數據庫的可用性。code
在不須要強一致性的業務場景下,均可以經過定時任務
+冪等操做
來實現最終一致性。blog
以上。事務