基于MySQL和DynamoDB的强一致性分布式事务实践
點擊上方“朱小廝的博客”,選擇“設(shè)為星標(biāo)”
后臺回復(fù)"書",獲取
后臺回復(fù)“k8s”,可領(lǐng)取k8s資料
在單體應(yīng)用向微服務(wù)架構(gòu)轉(zhuǎn)型的過程中,本地事務(wù)已不再滿足系統(tǒng)一致性需求,為了解決這一問題,前人在對性能和數(shù)據(jù)一致性反復(fù)權(quán)衡的過程中總結(jié)了許多典型的協(xié)議和算法,各有優(yōu)劣。本文我們將深入探討 Freewheel 如何實現(xiàn)無單點故障的可擴展分布式事務(wù)實現(xiàn)模型。
1為什么需要分布式事務(wù)?
當(dāng)應(yīng)用程序有嚴(yán)格的數(shù)據(jù)一致性要求時,ACID 事務(wù)是必須的,如果一個事務(wù)涉及的所有操作能夠放在一個服務(wù)內(nèi)部,且共用一個數(shù)據(jù)庫,那么只用在一個方法里同一個事務(wù)下操作數(shù)據(jù)庫即可。然而為了提升系統(tǒng)整體的可靠性,方便各個模塊獨立演化,系統(tǒng)從單體應(yīng)用演進為微服務(wù)架構(gòu)。隨著數(shù)據(jù)體量的增長,數(shù)據(jù)源也從 MySQL 擴展到關(guān)系型數(shù)據(jù)庫 Amazon Aurora 和 NoSQL 數(shù)據(jù)庫 (Amazon DynamoDB),基于多樣化索引和查詢數(shù)據(jù)的需求,引入了搜素引擎 (ApacheSolr 和 ElasticSearch ) ,多服務(wù)交互、多數(shù)據(jù)源并存產(chǎn)生了分布式事務(wù)。
Freewheel 分布式事務(wù)應(yīng)用場景有三個:
多服務(wù),同數(shù)據(jù)源: 業(yè)務(wù)單元跨越多個獨立服務(wù),服務(wù)訪問同一個數(shù)據(jù)源,如 MySQL。
單服務(wù),不同數(shù)據(jù)源: 業(yè)務(wù)單元涉及一個獨立服務(wù),但這個服務(wù)訪問多個數(shù)據(jù)源,如 MySQL,DynamoDB。
多服務(wù),不同數(shù)據(jù)源: 業(yè)務(wù)單元跨越多個獨立服務(wù),每個服務(wù)訪問不同數(shù)據(jù)源,如 MySQL,DynamoDB。
綜合考慮 Freewheel 的業(yè)務(wù)需求后,我們實現(xiàn)了多引擎數(shù)據(jù)庫分布式事務(wù)。
2多引擎數(shù)據(jù)庫分布式事務(wù)設(shè)計
Freewheel 分布式事務(wù)方案主要設(shè)計目標(biāo)如下:
數(shù)據(jù)強一致性: 確保該事務(wù)范圍內(nèi)的所有操作都可以全部成功或者全部失敗,事務(wù)具有原子性、一致性、隔離性、持久性 4 個特性。
Atomicity(原子性):一個事務(wù)中的所有操作,要么全部完成,要么全部不完成,不會結(jié)束在中間某個環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯誤,會被恢復(fù)到事務(wù)開始前的狀態(tài),就像這個事務(wù)從來沒有執(zhí)行過一樣。
Consistency(一致性):在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫的完整性沒有被破壞。完整性包括外鍵約束、應(yīng)用定義等約束不會被破壞。
Isolation(隔離性):數(shù)據(jù)庫允許多個并發(fā)事務(wù)同時對其數(shù)據(jù)進行讀寫和修改的能力,隔離性可以防止多個事務(wù)并發(fā)執(zhí)行時由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。
Durability(持久性):事務(wù)處理結(jié)束后,對數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會丟失。
系統(tǒng)高可用: 遵循“design for failure”的設(shè)計原則,硬件層面,采用服務(wù)節(jié)點多 region 多 AZ 部署和節(jié)點故障快速自恢復(fù)的策略來保證系統(tǒng)的高可用。軟件層面,設(shè)計 failover 機制應(yīng)對服務(wù)異常。
可擴展: 應(yīng)用 Auto Scaling 服務(wù),它會基于設(shè)定的負(fù)載壓力,自動進行擴展和縮容,來保證服務(wù)正常運行。
易用性: 分布式事務(wù)應(yīng)用 API:易學(xué),易懂,易記,系統(tǒng)設(shè)計上無業(yè)務(wù)侵入,沒有額外的編碼或測試工作。
3多引擎數(shù)據(jù)庫分布式事務(wù)技術(shù)選型
常見分布式事務(wù)解決方案對照表:
結(jié)合 Freewheel 強一致性業(yè)務(wù)需求,多數(shù)據(jù)源分布式事務(wù)將由 XA、2PC 和 Seata 這些解決方案組合而成。
Seata 框架設(shè)計思想
基于 XA 的 Aurora 分支事務(wù)
基于 2PC 的 DynamoDB 分支事務(wù)
4多數(shù)據(jù)源分布式事務(wù)解決方案
架構(gòu)解析
Freewheel 分布式事務(wù)依托在 Freewheel 數(shù)據(jù)訪問層中間件 (DAL) 上,這個中間件是由 Freewheel 平臺團隊自主研發(fā)的,它的目標(biāo)是為上游應(yīng)用提供更好的數(shù)據(jù)訪問,為下游數(shù)據(jù)源提供更好的保護。為了方便描述,下文均用 DAL 來作為 Freewheel 數(shù)據(jù)訪問層中間件的簡稱。
分布式事務(wù)由這三個組件來協(xié)商處理:
Transaction Coordinator (TC):?事務(wù)協(xié)調(diào)器,維護全局事務(wù)的運行狀態(tài),負(fù)責(zé)協(xié)調(diào)并驅(qū)動全局事務(wù)的提交或回滾。
Transaction Manager (TM):?控制全局事務(wù)的邊界,負(fù)責(zé)開啟一個全局事務(wù),并最終發(fā)起全局提交或全局回滾的決議。
Resource Manager (RM):?控制分支事務(wù),負(fù)責(zé)分支注冊、并接收事務(wù)協(xié)調(diào)器的指令,驅(qū)動分支(本地)事務(wù)的提交和回滾。
為了預(yù)防死鎖,并且減少 DAL RM 和 TC 之間的 交互,降低對 TC 的依賴,同一個分布式事務(wù)操作放在同一個 DAL 節(jié)點,由此,DAL RM 可以方便的在單節(jié)點控制和協(xié)調(diào)分支事務(wù),完成全局事務(wù)的提交和回滾。
以多服務(wù),不同數(shù)據(jù)源 (Aurora 與 DynamoDB) 為例,描述 Freewheel 分布式事務(wù)過程。
A Service TM 向 TC 申請開啟一個全局事務(wù),全局事務(wù)創(chuàng)建成功并生成一個全局唯一的 XID。
XID 在微服務(wù)調(diào)用鏈路的上下文中傳播。
TM 向 TC 發(fā)起針對 XID 的全局提交或回滾決議。
TC 向 DAL RM 發(fā)起全局提交或回滾決議。
DAL RM 對 XID 下管轄的全部分支事務(wù)完成提交或回滾請求。
數(shù)據(jù)訪問層資源管理器 (DAL RM) 實現(xiàn)
基于業(yè)務(wù)需求,DAL 分布式事務(wù)支持的數(shù)據(jù)源為 MySQL 和 AWS DynamoDB,下面章節(jié)闡述了這兩個數(shù)據(jù)源 ACID 技術(shù)實現(xiàn)。
分布式事務(wù)設(shè)計中新建了事務(wù)控制表、事務(wù)記錄表、索引表及業(yè)務(wù)鏡像表:
事務(wù)控制表:記錄事務(wù)運行狀態(tài)開始、提交、回滾。
事務(wù)記錄表:存儲正在發(fā)生事務(wù)信息。
索引表:記錄記錄主鍵 ID, 用于實現(xiàn)排它性對其他事務(wù)更新與普通更新。
業(yè)務(wù)鏡像表:存儲業(yè)務(wù)原值。
?Aurora/MySQL
采用 MySQL XA 2PC 來保證 ACID,原因如下:
Mysql 5.7 版本已經(jīng)支持 XA,目前 Aurora 也是 Mysql 5.7.x。
XA 強一致性。
不侵入業(yè)務(wù),這會減少業(yè)務(wù)方的工作量。
這個時序圖描述了 RM 對 MySQL 事務(wù)的工作流程:
一個事務(wù)操作,由同一個 DAL RM 處理,相同 DB 下業(yè)務(wù)事務(wù)處理,放在一個 XA 操作里:
節(jié)省 XA 連接 fd。
減少 DAL RM 和 Aurora 之間的 XA 交互次數(shù)。
避免多個 XA 分支事務(wù)上的數(shù)據(jù)操作沖突。
SQL CRUD 語句應(yīng)該使用觸發(fā)行鎖的索引操作,否則會觸發(fā)表鎖,影響系統(tǒng)吞吐量。
?AWS DynamoDB
DynamoDB 提供了本地事務(wù)接口 TransactGetItems 和 TransactWriteItems, 它等效于 MySQL 批量操作,對于相互間有上下文或者依賴的操作并不可用,這限制了它在應(yīng)用中的使用場景,詳細(xì)信息請參考 TransactGetItems 和 TransactWriteItems。
DynamoDB 本身沒有分布式事務(wù)機制,DAL 結(jié)合 DynamoDB 功能屬性,對提供的插入、更新、刪除和查詢接口,設(shè)計 2PC 機制 來滿足 DynamoDB 的 事務(wù)屬性。
下表顯示了分布式事務(wù)操作 (DisTxDAL) 和其他操作之間的隔離級別。
更新接口實現(xiàn)方法
一階段
應(yīng)用本地事務(wù)原子地備份事務(wù)記錄及備份索引
應(yīng)用本地事務(wù)原子地更新附加事務(wù)信息業(yè)務(wù)值及備份業(yè)務(wù)原值到鏡像表
二階段
如果決議是提交,應(yīng)用本地事務(wù)原子地移除業(yè)務(wù)記錄事務(wù)屬性、刪除鏡像記錄、刪除備份事務(wù)記錄
如果決議是回滾,應(yīng)用本地事務(wù)原子地恢復(fù)業(yè)務(wù)記錄、刪除鏡像記錄、刪除備份事務(wù)記錄
插入接口實現(xiàn)方法
一階段
應(yīng)用本地事務(wù)原子地備份事務(wù)記錄及備份索引
插入帶有事務(wù)屬性信息的業(yè)務(wù)記錄
二階段
如果決議是提交,應(yīng)用本地事務(wù)原子地移除業(yè)務(wù)事務(wù)屬性、刪除備份記錄
如果決議是回滾,應(yīng)用本地事務(wù)原子地刪除業(yè)務(wù)記錄、刪除備份記錄
刪除接口實現(xiàn)方法
一階段
應(yīng)用本地事務(wù)原子地備份事物記錄及備份索引
更新帶有事務(wù)屬性信息的業(yè)務(wù)記錄, 標(biāo)注為刪除操作
二階段
如果決議是提交,應(yīng)用本地事務(wù)原子地刪除事務(wù)記錄、刪除業(yè)務(wù)記錄
如果決議是回滾,應(yīng)用本地事務(wù)原子地恢復(fù)業(yè)務(wù)記錄、刪除備份記錄
查詢接口實現(xiàn)方法
事務(wù)進行中的數(shù)據(jù)含有事務(wù)屬性信息,xid 表示事務(wù)全局事務(wù) ID, operation 表示事務(wù)操作接口 create、update、delete,這里 item 表示業(yè)務(wù)數(shù)據(jù)元素。
基于業(yè)務(wù)數(shù)據(jù)變更表及寫接口實現(xiàn)方法,實現(xiàn)了在讀提交與讀未提及查詢方法:
判斷記錄是否含有事務(wù)屬性,如果無,返回記錄,否則到步驟 2
判讀隔離級別,如果讀提交,步驟 3,如果讀未提交,步驟 5
記錄事務(wù)操作是 create, 返回空,否則如果是 delete,去除事物屬性信息,然后返回,否則步驟 4
本地事務(wù)原子地讀取鏡像表與業(yè)務(wù)表,如果鏡像表值存在,返回,否則返回業(yè)務(wù)表值,都不存在返回空
如果記錄事務(wù)操作是 delete,返回空,否則返回記錄
數(shù)據(jù)訪問層事務(wù)管理器 (DAL TM) 實現(xiàn)
為了方便用戶使用,分布式事務(wù) API 里封裝了與事務(wù)協(xié)調(diào)器及 DAL 資源管理器的交互過程,交互過程對應(yīng)用是透明的,下面是分布式事務(wù) API:
type DistributedTransApi interface {DisTxDAL(ctx context.Context, fn TranFunc) error } type TranFunc func(ctx context.Context) error微服務(wù)之間,微服務(wù)與數(shù)據(jù)庫訪問層之間采用 google rpc 調(diào)用,服務(wù)之間關(guān)鍵數(shù)據(jù)都是基于 context metadata,如果經(jīng)過兩層服務(wù)交互, 就會導(dǎo)致 context metadata 丟失。舉例來說,A 服務(wù)調(diào)用 B 服務(wù),B 服務(wù)調(diào)用 C 服務(wù),那么 C 服務(wù)就會缺失 A 服務(wù) context metadata,針對這種情況,DAL 提供了通用函數(shù)用于提取 DAL 相關(guān)的元數(shù)據(jù),供應(yīng)用方按需添加。
func ExtractDalMetadata(ctx context.Context) (metadata.MD, error)數(shù)據(jù)訪問層事務(wù)協(xié)調(diào)器 (DAL TC) 實現(xiàn)?
協(xié)調(diào)器主要功能點:
分配事務(wù) XID,維護全局事務(wù)的運行狀態(tài),負(fù)責(zé)協(xié)調(diào)和驅(qū)動全局事務(wù)的提交或回滾;
故障轉(zhuǎn)移:提交 / 回滾。
?分配事務(wù) XID
全局分配唯一事務(wù) ID,供 DAL TM 獲取,此數(shù)據(jù)需要在同一事務(wù)的業(yè)務(wù)服務(wù)間傳輸共享。
?事務(wù)協(xié)調(diào)
維護全局事務(wù)的運行狀態(tài),負(fù)責(zé)協(xié)調(diào)和驅(qū)動全局事務(wù)的提交或回滾。
?故障轉(zhuǎn)移
DAL TC 是 HA 多節(jié)點實例,引入 ETCD leader 選舉機制來保證只有一個 TC 實例承擔(dān) failover 功能,詳細(xì)信息在系統(tǒng)高可用章節(jié)軟件層面 failover。
系統(tǒng)高可用
?硬件層面
為了預(yù)防硬件故障對高可用的影響,DAL,TC 和 ETCD 服務(wù)均是多 Region 多 AZ 部署,并且基于服務(wù)的特性配置了相應(yīng)的服務(wù)策略:
ETCD 采用自恢復(fù)策略
DAL 和 TC 服務(wù)采用了彈性伸縮策略
美東美西均部署相應(yīng)服務(wù)作為服務(wù)災(zāi)備策略,下圖是美東地區(qū)的解釋圖(美西類似)。
?軟件層面 Failover
在 DAL 應(yīng)用或者業(yè)務(wù)應(yīng)用遇到異常退出時,軟件層面 Failover 機制是為了能不發(fā)生死鎖,并且繼續(xù)處理未完成分布式事務(wù),實現(xiàn)方法如下:
DAL TM 接口添加超時控制,由應(yīng)用設(shè)置事務(wù)的超時時間,默認(rèn)是 60 秒
DAL RM 在事物開始、提交或者回滾階段存儲 xid 信息,如過期時間,運行狀態(tài)等,在業(yè)務(wù)接口調(diào)用里存儲業(yè)務(wù)處理記錄
DAL TC 輪詢地從事務(wù)控制表里獲取超時事務(wù)。基于事務(wù)狀態(tài)處理:如果 start or rollback:觸發(fā) Rollback,如果 commit:觸發(fā) commit
5未來展望
Freewheel 強一致性分布式事務(wù)未來會支撐更多的數(shù)據(jù)源,如 Redis、Solr 和 ElasticSearch 等,目前的數(shù)據(jù)庫訪問層 API 是基于 gRPC,這對數(shù)據(jù)庫訪問層使用方帶來了一定技術(shù)語言限制,未來會探究 GraphQL 在數(shù)據(jù)庫訪問層分布式事務(wù)應(yīng)用的可行性。
想知道更多?掃描下面的二維碼關(guān)注我后臺回復(fù)"技術(shù)",加入技術(shù)群 后臺回復(fù)“k8s”,可領(lǐng)取k8s資料【精彩推薦】ClickHouse到底是什么?為什么如此牛逼!
原來ElasticSearch還可以這么理解
面試官:InnoDB中一棵B+樹可以存放多少行數(shù)據(jù)?
架構(gòu)之道:分離業(yè)務(wù)邏輯和技術(shù)細(xì)節(jié)
星巴克不使用兩階段提交
面試官:Redis新版本開始引入多線程,談?wù)勀愕目捶?#xff1f;
喜馬拉雅自研網(wǎng)關(guān)架構(gòu)演進過程
收藏:存儲知識全面總結(jié)
微博千萬級規(guī)模高性能高并發(fā)的網(wǎng)絡(luò)架構(gòu)設(shè)計
總結(jié)
以上是生活随笔為你收集整理的基于MySQL和DynamoDB的强一致性分布式事务实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 搞定 Go 语言,不会这些可不行
- 下一篇: 深入理解 MySQL 索引底层原理