又一個(gè)高性能的云原生數(shù)據(jù)庫開源了DBPack
2022 年 4 月,中國電子云開源了其云原生數(shù)據(jù)庫 Mesh 項(xiàng)目 DBPack。該項(xiàng)目的誕生,旨在解決用戶上云過程中面臨的一些技術(shù)難點(diǎn),諸如分布式事務(wù)、分庫分表等。由于它數(shù)據(jù)庫 Mesh 的定位,意味著它可以支持任意微服務(wù)編程語言。
1 分布式事務(wù)
DBPack 的分布式事務(wù)致力于實(shí)現(xiàn)對用戶的業(yè)務(wù)無入侵,它對 HTTP 流量和 MYSQL 流量做了攔截代理,支持 AT 模式(自動(dòng)補(bǔ)償 SQL)和 TCC 模式(自動(dòng)補(bǔ)償 HTTP 請求)。
DBPack 從 Kubernetes control loop 思想中獲得靈感,采用 ETCD Watch 機(jī)制來驅(qū)動(dòng)分布式事務(wù)提交回滾。在采用代理使連接增加一跳的情況下,它的性能相比采用 MYSQL 存儲的分布式事務(wù)解決方案 seata-golang 性能提高了百分之 50。

2 AT 模式
AT 模式的性能取決于全局鎖的釋放速度,哪個(gè)事務(wù)競爭到了全局鎖就能對業(yè)務(wù)數(shù)據(jù)做修改,在單位時(shí)間內(nèi),全局鎖的釋放速度越快,競爭到鎖的事務(wù)越多,性能越高。從 ETCD 官方 Bench 測試數(shù)據(jù)中可以看到,ETCD 在高并發(fā)下,讀寫延遲很低,不同并發(fā)壓力下寫延遲 2 毫秒到 20 毫秒不等,讀延遲基本在 10 毫秒以內(nèi)。采用 ETCD 來存儲全局鎖是 DBPack 分布式事務(wù)性能提升的關(guān)鍵。

上圖展示了 seata-golang 協(xié)調(diào)一個(gè)分布式事務(wù)的交互邏輯。從圖上我們可以看出,事務(wù)發(fā)起者(TM)和事務(wù)協(xié)調(diào)者(TC)間存在創(chuàng)建(開始)全局事務(wù)、提交(回滾)全局事務(wù) RPC 交互。事務(wù)參與者(RM)和事務(wù)協(xié)調(diào)者(TC)間存在注冊分支事務(wù)、報(bào)告分支事務(wù)執(zhí)行狀態(tài) RPC 交互。事務(wù)協(xié)調(diào)者(TC)和 MYSQL 交互保存狀態(tài)數(shù)據(jù)。
而 DBPack 創(chuàng)建全局事務(wù)、注冊分支事務(wù)只是在 ETCD 插入兩條 KV 數(shù)據(jù),事務(wù)提交回滾時(shí)修改對應(yīng)數(shù)據(jù)的狀態(tài),DBPack Sidecar 通過 ETCD Watch 機(jī)制感知到數(shù)據(jù)的變化就能立即處理數(shù)據(jù)的提交回滾,從而在交互上減少了很多 RPC 請求。

各 Sidecar Watch 應(yīng)用產(chǎn)生的數(shù)據(jù),各自處理,實(shí)際上已經(jīng)沒有中心化的事務(wù)協(xié)調(diào)者,架構(gòu)也變得簡單了。核心的事務(wù)協(xié)調(diào)邏輯代碼包括配置代碼都比 Seata-golang 大幅減少。所以 DBPack 以全新的云原生的思路,帶了更簡潔的架構(gòu)和更高的性能。
seata-golang 事務(wù)協(xié)調(diào)核心代碼
dbpack 事務(wù)協(xié)調(diào)核心代碼
DBPack 支持所有微服務(wù)編程語言,samples 中已提供了 Go 語言和 Java 語言的例子,PHP 和 Python 的例子也在開發(fā)中。
3 TCC 模式
提到 TCC 模式,大家可能第一時(shí)間想到 TCC 模式可能存在的問題:冪等性、防懸掛等。事務(wù)懸掛產(chǎn)生的原因是什么?其實(shí)這是一個(gè)很偽的問題!

APP1 在調(diào)用 APP2 的 Prepare 方法之前,事務(wù)框架根據(jù)上下問信息,自動(dòng)把 Commit、Cancel 需要執(zhí)行的方法名以及 Prepare 方法執(zhí)行的上下文告訴事務(wù)協(xié)調(diào)者(注冊分支事務(wù)),再執(zhí)行 Prepare 方法。如果執(zhí)行 APP1 調(diào)用 APP2 的 Prepare 方法的時(shí)候,發(fā)生網(wǎng)絡(luò)問題,導(dǎo)致 APP2 遲遲沒有收到 Prepare 請求,事務(wù)協(xié)調(diào)者經(jīng)過一定時(shí)間后,認(rèn)為全局事務(wù)超時(shí),則 TC 根據(jù)注冊上來的事務(wù)分支信息發(fā)起全局回滾,此時(shí),APP1 向 APP2 發(fā)起一個(gè) Cancel 請求,很巧的是,APP2 端 Cancel 請求比 Prepare 請求先到達(dá),事務(wù)空回滾后,再收到 Prepare 請求,Prepare 如果正常執(zhí)行了,那就完了,全局事務(wù)已經(jīng)回滾了,這個(gè) Prepare 操作永遠(yuǎn)也不會提交、回滾,事務(wù)掛起了,數(shù)據(jù)不一致了。
首先,這種概率很小,其次,為什么一定要在 Prepare 網(wǎng)絡(luò)請求之前注冊分支事務(wù),可不可以在 APP2 收到 Prepare 請求執(zhí)行業(yè)務(wù)代碼之前注冊,這時(shí)候一定能確定 Prepare 請求已經(jīng)到了,Cancel 請求確定能在 Prepare 請求之后發(fā)生,是不是就不存在懸掛問題了。
實(shí)際上 seata-golang 誕生之時(shí)就支持在分支業(yè)務(wù)執(zhí)行端注冊 TCC 分支事務(wù),但大家可能沒有深入思考這個(gè)問題,機(jī)械地認(rèn)為事務(wù)懸掛必然會發(fā)生。
DBPack 也是在請求到達(dá) sidecar 后再注冊 TCC 事務(wù)分支,確保 Prepare 先于 Cancel 執(zhí)行。有人說因?yàn)?CPU 調(diào)度的原因,還是可能出現(xiàn) Cancel 先于 Prepare 執(zhí)行的情況,但這種概率非常非常低。具體到操作的業(yè)務(wù)數(shù)據(jù),建議使用 XID 和 BranchID 加鎖。
4 讀寫分離
DBPack 當(dāng)前支持對 SQL 請求自動(dòng)路由,寫請求路由到寫庫,讀請求路由到讀庫。在開啟事務(wù)的情況下,請求自動(dòng)路由到寫庫。同時(shí),也可以通過 SQL Hint 自動(dòng)路由讀請求到用戶指定的數(shù)據(jù)庫。
5 分庫分表
分庫分表的功能目前還在開發(fā)中,當(dāng)前已經(jīng)支持跨分片、跨 DB 的查詢請求,支持 Order By 和 Limit。
6 結(jié)語
更多特性我們也在積極開發(fā)中,DBPack 社區(qū)非常 Open,進(jìn)入到社區(qū)我們都是平等的開源愛好者,在這里你也可以成長為大佬,歡迎感興趣的同學(xué)與我們一起建設(shè) DBPack 社區(qū)。
進(jìn)群或參與社區(qū)建設(shè)請?zhí)砑游⑿牛?em>scottlewis。
鏈接
DBPack 項(xiàng)目地址:
https://github.com/cectc/dbpack
DBPack 文檔:
https://cectc.github.io/dbpack-doc/#/
