基于 MySQL 和 DynamoDB 的強(qiáng)一致性分布式事務(wù)實(shí)踐

在單體應(yīng)用向微服務(wù)架構(gòu)轉(zhuǎn)型的過程中,本地事務(wù)已不再滿足系統(tǒng)一致性需求,為了解決這一問題,前人在對(duì)性能和數(shù)據(jù)一致性反復(fù)權(quán)衡的過程中總結(jié)了許多典型的協(xié)議和算法,各有優(yōu)劣。本文我們將深入探討 Freewheel 如何實(shí)現(xiàn)無單點(diǎn)故障的可擴(kuò)展分布式事務(wù)實(shí)現(xiàn)模型。

為什么需要分布式事務(wù)?

當(dāng)應(yīng)用程序有嚴(yán)格的數(shù)據(jù)一致性要求時(shí),ACID 事務(wù)是必須的,如果一個(gè)事務(wù)涉及的所有操作能夠放在一個(gè)服務(wù)內(nèi)部,且共用一個(gè)數(shù)據(jù)庫,那么只用在一個(gè)方法里同一個(gè)事務(wù)下操作數(shù)據(jù)庫即可。然而為了提升系統(tǒng)整體的可靠性,方便各個(gè)模塊獨(dú)立演化,系統(tǒng)從單體應(yīng)用演進(jìn)為微服務(wù)架構(gòu)。隨著數(shù)據(jù)體量的增長,數(shù)據(jù)源也從 MySQL 擴(kuò)展到關(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)用場(chǎng)景有三個(gè):

多服務(wù),同數(shù)據(jù)源: 業(yè)務(wù)單元跨越多個(gè)獨(dú)立服務(wù),服務(wù)訪問同一個(gè)數(shù)據(jù)源,如 MySQL。

單服務(wù),不同數(shù)據(jù)源: 業(yè)務(wù)單元涉及一個(gè)獨(dú)立服務(wù),但這個(gè)服務(wù)訪問多個(gè)數(shù)據(jù)源,如 MySQL,DynamoDB。

多服務(wù),不同數(shù)據(jù)源: 業(yè)務(wù)單元跨越多個(gè)獨(dú)立服務(wù),每個(gè)服務(wù)訪問不同數(shù)據(jù)源,如 MySQL,DynamoDB。

綜合考慮 Freewheel 的業(yè)務(wù)需求后,我們實(shí)現(xiàn)了多引擎數(shù)據(jù)庫分布式事務(wù)。

多引擎數(shù)據(jù)庫分布式事務(wù)設(shè)計(jì)

Freewheel 分布式事務(wù)方案主要設(shè)計(jì)目標(biāo)如下:

數(shù)據(jù)強(qiáng)一致性:確保該事務(wù)范圍內(nèi)的所有操作都可以全部成功或者全部失敗,事務(wù)具有原子性、一致性、隔離性、持久性 4 個(gè)特性。

Atomicity(原子性):一個(gè)事務(wù)中的所有操作,要么全部完成,要么全部不完成,不會(huì)結(jié)束在中間某個(gè)環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯(cuò)誤,會(huì)被恢復(fù)到事務(wù)開始前的狀態(tài),就像這個(gè)事務(wù)從來沒有執(zhí)行過一樣。

Consistency(一致性):在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫的完整性沒有被破壞。完整性包括外鍵約束、應(yīng)用定義等約束不會(huì)被破壞。

Isolation(隔離性):數(shù)據(jù)庫允許多個(gè)并發(fā)事務(wù)同時(shí)對(duì)其數(shù)據(jù)進(jìn)行讀寫和修改的能力,隔離性可以防止多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。

Durability(持久性):事務(wù)處理結(jié)束后,對(duì)數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會(huì)丟失。

系統(tǒng)高可用:遵循“design for failure”的設(shè)計(jì)原則,硬件層面,采用服務(wù)節(jié)點(diǎn)多 region 多 AZ 部署和節(jié)點(diǎn)故障快速自恢復(fù)的策略來保證系統(tǒng)的高可用。軟件層面,設(shè)計(jì) failover 機(jī)制應(yīng)對(duì)服務(wù)異常。

可擴(kuò)展:應(yīng)用 Auto Scaling 服務(wù),它會(huì)基于設(shè)定的負(fù)載壓力,自動(dòng)進(jìn)行擴(kuò)展和縮容,來保證服務(wù)正常運(yùn)行。

易用性:分布式事務(wù)應(yīng)用 API:易學(xué),易懂,易記,系統(tǒng)設(shè)計(jì)上無業(yè)務(wù)侵入,沒有額外的編碼或測(cè)試工作。

多引擎數(shù)據(jù)庫分布式事務(wù)技術(shù)選型

結(jié)合 Freewheel 強(qiáng)一致性業(yè)務(wù)需求,多數(shù)據(jù)源分布式事務(wù)將由 XA、2PC 和 Seata 這些解決方案組合而成。

Seata 框架設(shè)計(jì)思想

基于 XA 的 Aurora 分支事務(wù)

基于 2PC 的 DynamoDB 分支事務(wù)

多數(shù)據(jù)源分布式事務(wù)解決方案

架構(gòu)解析

Freewheel 分布式事務(wù)依托在 Freewheel 數(shù)據(jù)訪問層中間件(DAL)上,這個(gè)中間件是由 Freewheel 平臺(tái)團(tuán)隊(duì)自主研發(fā)的,它的目標(biāo)是為上游應(yīng)用提供更好的數(shù)據(jù)訪問,為下游數(shù)據(jù)源提供更好的保護(hù)。為了方便描述,下文均用 DAL 來作為 Freewheel 數(shù)據(jù)訪問層中間件的簡稱。

分布式事務(wù)由這三個(gè)組件來協(xié)商處理:

Transaction Coordinator (TC): 事務(wù)協(xié)調(diào)器,維護(hù)全局事務(wù)的運(yùn)行狀態(tài),負(fù)責(zé)協(xié)調(diào)并驅(qū)動(dòng)全局事務(wù)的提交或回滾。

Transaction Manager (TM): 控制全局事務(wù)的邊界,負(fù)責(zé)開啟一個(gè)全局事務(wù),并最終發(fā)起全局提交或全局回滾的決議。

Resource Manager (RM): 控制分支事務(wù),負(fù)責(zé)分支注冊(cè)、并接收事務(wù)協(xié)調(diào)器的指令,驅(qū)動(dòng)分支(本地)事務(wù)的提交和回滾。

為了預(yù)防死鎖,并且減少 DAL RM 和 TC 之間的 交互,降低對(duì) TC 的依賴,同一個(gè)分布式事務(wù)操作放在同一個(gè) DAL 節(jié)點(diǎn),由此,DAL RM 可以方便的在單節(jié)點(diǎn)控制和協(xié)調(diào)分支事務(wù),完成全局事務(wù)的提交和回滾。

以多服務(wù),不同數(shù)據(jù)源(Aurora 與 DynamoDB)為例,描述 Freewheel 分布式事務(wù)過程。

A Service TM 向 TC 申請(qǐng)開啟一個(gè)全局事務(wù),全局事務(wù)創(chuàng)建成功并生成一個(gè)全局唯一的 XID。

XID 在微服務(wù)調(diào)用鏈路的上下文中傳播。

TM 向 TC 發(fā)起針對(duì) XID 的全局提交或回滾決議。

TC 向 DAL RM 發(fā)起全局提交或回滾決議。

DAL RM 對(duì) XID 下管轄的全部分支事務(wù)完成提交或回滾請(qǐng)求。


數(shù)據(jù)訪問層資源管理器(DAL RM)實(shí)現(xiàn)

基于業(yè)務(wù)需求,DAL 分布式事務(wù)支持的數(shù)據(jù)源為 MySQL 和 AWS DynamoDB,下面章節(jié)闡述了這兩個(gè)數(shù)據(jù)源 ACID 技術(shù)實(shí)現(xiàn)。

分布式事務(wù)設(shè)計(jì)中新建了事務(wù)控制表、事務(wù)記錄表、索引表及業(yè)務(wù)鏡像表:

事務(wù)控制表:記錄事務(wù)運(yùn)行狀態(tài)開始、提交、回滾。

事務(wù)記錄表:存儲(chǔ)正在發(fā)生事務(wù)信息。

索引表:記錄記錄主鍵 ID, 用于實(shí)現(xiàn)排它性對(duì)其他事務(wù)更新與普通更新。

業(yè)務(wù)鏡像表:存儲(chǔ)業(yè)務(wù)原值。

Aurora/MySQL

采用 MySQL XA 2PC 來保證 ACID,原因如下:

Mysql 5.7 版本已經(jīng)支持 XA,目前 Aurora 也是 Mysql 5.7.x。

XA 強(qiáng)一致性。

不侵入業(yè)務(wù),這會(huì)減少業(yè)務(wù)方的工作量。

這個(gè)時(shí)序圖描述了 RM 對(duì) MySQL 事務(wù)的工作流程:

一個(gè)事務(wù)操作,由同一個(gè) DAL RM 處理,相同 DB 下業(yè)務(wù)事務(wù)處理,放在一個(gè) XA 操作里:

節(jié)省 XA 連接 fd。

減少 DAL RM 和 Aurora 之間的 XA 交互次數(shù)。

避免多個(gè) XA 分支事務(wù)上的數(shù)據(jù)操作沖突。

SQL CRUD 語句應(yīng)該使用觸發(fā)行鎖的索引操作,否則會(huì)觸發(fā)表鎖,影響系統(tǒng)吞吐量。

AWS DynamoDB

DynamoDB 提供了本地事務(wù)接口 TransactGetItems 和 TransactWriteItems, 它等效于 MySQL 批量操作,對(duì)于相互間有上下文或者依賴的操作并不可用,這限制了它在應(yīng)用中的使用場(chǎng)景,詳細(xì)信息請(qǐng)參考 TransactGetItems 和 TransactWriteItems。

DynamoDB 本身沒有分布式事務(wù)機(jī)制,DAL 結(jié)合 DynamoDB 功能屬性,對(duì)提供的插入、更新、刪除和查詢接口,設(shè)計(jì) 2PC 機(jī)制 來滿足 DynamoDB 的 事務(wù)屬性。

下表顯示了分布式事務(wù)操作 (DisTxDAL) 和其他操作之間的隔離級(jí)別。

更新接口實(shí)現(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ù)記錄

插入接口實(shí)現(xiàn)方法

一階段

應(yīng)用本地事務(wù)原子地備份事務(wù)記錄及備份索引

插入帶有事務(wù)屬性信息的業(yè)務(wù)記錄

二階段

如果決議是提交,應(yīng)用本地事務(wù)原子地移除業(yè)務(wù)事務(wù)屬性、刪除備份記錄

如果決議是回滾,應(yīng)用本地事務(wù)原子地刪除業(yè)務(wù)記錄、刪除備份記錄

刪除接口實(shí)現(xiàn)方法

一階段

應(yīng)用本地事務(wù)原子地備份事物記錄及備份索引

更新帶有事務(wù)屬性信息的業(yè)務(wù)記錄, 標(biāo)注為刪除操作

二階段

如果決議是提交,應(yīng)用本地事務(wù)原子地刪除事務(wù)記錄、刪除業(yè)務(wù)記錄

如果決議是回滾,應(yīng)用本地事務(wù)原子地恢復(fù)業(yè)務(wù)記錄、刪除備份記錄

查詢接口實(shí)現(xiàn)方法

事務(wù)進(jìn)行中的數(shù)據(jù)含有事務(wù)屬性信息,xid 表示事務(wù)全局事務(wù) ID, operation 表示事務(wù)操作接口 create、update、delete,這里 item 表示業(yè)務(wù)數(shù)據(jù)元素。

操作接口事務(wù)開始事務(wù)進(jìn)行事務(wù)結(jié)束(提交)事務(wù)結(jié)束(回滾)

基于業(yè)務(wù)數(shù)據(jù)變更表及寫接口實(shí)現(xiàn)方法,實(shí)現(xiàn)了在讀提交與讀未提及查詢方法:

判斷記錄是否含有事務(wù)屬性,如果無,返回記錄,否則到步驟 2

判讀隔離級(jí)別,如果讀提交,步驟 3,如果讀未提交,步驟 5

記錄事務(wù)操作是 create,返回空,否則如果是 delete,去除事物屬性信息,然后返回,否則步驟 4

本地事務(wù)原子地讀取鏡像表與業(yè)務(wù)表,如果鏡像表值存在,返回,否則返回業(yè)務(wù)表值,都不存在返回空

如果記錄事務(wù)操作是 delete,返回空,否則返回記錄

數(shù)據(jù)訪問層事務(wù)管理器(DAL TM)實(shí)現(xiàn)

為了方便用戶使用,分布式事務(wù) API 里封裝了與事務(wù)協(xié)調(diào)器及 DAL 資源管理器的交互過程,交互過程對(duì)應(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ù)交互,就會(huì)導(dǎo)致 context metadata 丟失。舉例來說,A 服務(wù)調(diào)用 B 服務(wù),B 服務(wù)調(diào)用 C 服務(wù),那么 C 服務(wù)就會(huì)缺失 A 服務(wù) context metadata,針對(duì)這種情況,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)實(shí)現(xiàn)

協(xié)調(diào)器主要功能點(diǎn):

分配事務(wù) XID,維護(hù)全局事務(wù)的運(yùn)行狀態(tài),負(fù)責(zé)協(xié)調(diào)和驅(qū)動(dòng)全局事務(wù)的提交或回滾;

故障轉(zhuǎn)移:提交/回滾。

分配事務(wù) XID

全局分配唯一事務(wù) ID,供 DAL TM 獲取,此數(shù)據(jù)需要在同一事務(wù)的業(yè)務(wù)服務(wù)間傳輸共享。

事務(wù)協(xié)調(diào)

維護(hù)全局事務(wù)的運(yùn)行狀態(tài),負(fù)責(zé)協(xié)調(diào)和驅(qū)動(dòng)全局事務(wù)的提交或回滾。

故障轉(zhuǎn)移

DAL TC 是 HA 多節(jié)點(diǎn)實(shí)例,引入 ETCD leader 選舉機(jī)制來保證只有一個(gè) TC 實(shí)力承擔(dān) failover 功能,詳細(xì)信息在系統(tǒng)高可用章節(jié)軟件層面 failover。

系統(tǒng)高可用

硬件層面

為了預(yù)防硬件故障對(duì)高可用的影響,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)用遇到異常退出時(shí),軟件層面 Failover 機(jī)制是為了能不發(fā)生死鎖,并且繼續(xù)處理未完成分布式事務(wù),實(shí)現(xiàn)方法如下:

DAL TM 接口添加超時(shí)控制,由應(yīng)用設(shè)置事務(wù)的超時(shí)時(shí)間,默認(rèn)是 60 秒

DAL RM 在事物開始、提交或者回滾階段存儲(chǔ) xid 信息,如過期時(shí)間,運(yùn)行狀態(tài)等,在業(yè)務(wù)接口調(diào)用里存儲(chǔ)業(yè)務(wù)處理記錄

DAL TC 輪詢地從事務(wù)控制表里獲取超時(shí)事務(wù)?;谑聞?wù)狀態(tài)處理:如果 start or rollback:觸發(fā) Rollback,如果 commit:觸發(fā) commit

未來展望

Freewheel 強(qiáng)一致性分布式事務(wù)未來會(huì)支撐更多的數(shù)據(jù)源,如 Redis、Solr 和 ElasticSearch 等,目前的數(shù)據(jù)庫訪問層 API 是基于 gRPC,這對(duì)數(shù)據(jù)庫訪問層使用方帶來了一定技術(shù)語言限制,未來會(huì)探究 GraphQL 在數(shù)據(jù)庫訪問層分布式事務(wù)應(yīng)用的可行性。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容