歡迎你們前往騰訊雲技術社區,獲取更多騰訊海量技術實踐乾貨哦~
算法
近日,騰訊雲發佈了分佈式數據庫解決方案(DCDB),其最明顯的特性之一就是提供了高於開源分佈式事務XA的性能。大型業務系統有着用戶多、併發高的特色,在這方面,集中式數據庫(單機數據庫)的性能很難支持,所以主流的互聯網公司每每採用分佈式(架構)數據庫,物理上利用更多的低端設備,邏輯上對大表水平拆分支撐業務的須要。sql
雖然分佈式數據庫能解決性能難題,但事務一致性(Consistency)的問題,卻很難在分佈式數據庫上獲得解決。數據庫
衆所周知,一個事務所作的更新,分佈式數據庫系統內部多個獨立的數據節點完成(每一個節點的本地事務是這個全局事務的一個事務分支),在這樣一個全局事務提交期間,有可能某些事務分支沒法成功提交。後端
針對這一問題,雖然業內早已存在理論解決方案——二階段提交協議(簡稱2PC),並延伸出分佈式事務(簡稱XA)的解決方案。但業內卻少有工程化實現且大規模應用的案例。而騰訊雲分佈式數據庫DCDB,卻已在內部業務中應用多年。安全
(圖:二階段提交算法)bash
目前DCDB已應用在騰訊內部90%以上的交易、計費業務,而且三一重工(樹根互聯)、匯通天下(G7)、閱文集團(起點/創世中文網等)、微衆銀行、和泰人壽、威富通等都在該產品。網絡
騰訊雲分佈式數據庫DCDB,是基於騰訊金融級數據庫(公司內部代號TDSQL)雲化改造而來的兼容MySQL協議的分佈式數據庫。現現在,騰訊雲DCDB已經正式在MySQL 5.7(percona分支)協議上支持分佈式事務XA,並已在騰訊雲公有云、金融雲發佈供開發者使用。開發者能夠經過申請DCDB實例,並在初始化後,鏈接實例運行以下sql進行初始化:架構
MySQL> xa init;併發
Query OK, 0 rows affected (0.03 sec)
注意:初始化xa前,請開啓強同步複製能力,另外該sql會建立xa.gtid_log_t,用戶在後續使用中萬勿對其進行任何操做。運維
爲更好的支持分佈式事務,DCDB還新增了SQL命令:
1)SELECT gtid(),獲取當前分佈式事務的gtid(事務的全局惟一性標識),若是該事務不是分佈式事務則返回空;
gtid的格式:
‘網關id’-‘網關隨機值’-‘序列號’-‘時間戳’-‘分區號’,例如 c46535fe-b6-dd-595db6b8-252)SELECT gtid_state(「gtid」),獲取「gtid」的狀態,可能的結果有:
a)「COMMIT」,標識該事務已經或者最終會被提交
b)「ABORT」,標識該事務最終會被回滾
c) 空,因爲事務的狀態會在一個小時以後清楚,所以有如下兩種可能:
1) 一個小時以後查詢,標識事務狀態已經清除
2) 一個小時之內查詢,標識事務最終會被回滾3) 運維命令:
xa recover:向後端SET發送xa recover命令,並進行彙總
xa lockwait:顯示當前分佈式事務的等待關係(可使用dot命令將輸出轉化爲等待關係圖)
xa show:當前網關上正在運行的分佈式事務
以Python爲例,能夠對轉帳業務進行以下編碼:
db = pyMySQL.connect(host=testHost,
port=testPort, user=testUser, password=testPassword, database=testDatabase)
cursor = db.cursor()
try:
cursor.execute("begin")
#爲一個帳戶Bob的餘額減1
query = "update t_user_balance SET balance = balance - 1 where user='Bob' and balance>1) affected = cursor.execute(query) if affected == 0: #餘額不足,回滾事務 cursor.execute("rollback") return #爲一個帳戶John的餘額加1 query = "update t_user_balance SET balance = balance + 1 where user='John')
cursor.execute(query)
#爲了安全起見,建議在這裏執行‘SELECT gtid()’獲取當前事務的id值,便於後續跟蹤事務的執行狀況
#提交事務
cursor.execute("commit")
except pyMySQL.err.MySQLError as e:
#發生故障,回滾事務
cursor.execute("rollback")
複製代碼
分佈式事務的好處在於會大大下降應用開發難度,由於在某些不支持XA的數據庫中,須要業務系統經過特殊而且巧妙的設計,而非利用數據庫來解決事務中數據不一致等問題。這種對應用開發者的技術水平要求很高,越是複雜的業務系統,越會增長開發成本和技術門檻,這是業內大多數開發者面對分佈式數據庫時,只能望而卻步的主要緣由。
一、DCDB架構介紹
騰訊雲DCDB整個集羣架構簡圖以下圖,MySQL採用主從節點配置(也叫做主備)一套主從節點叫作SET,在每個SET外配置網關(TProxy),造成一個物理分片(Shard)。
DCDB後端是MySQL(或其分支版本)數據庫,目前騰訊雲公有云發佈支持XA的版本是基於MySQL 5.7.17(percona分支)。
二、網關(TProxy)與XA
網關是用於接收請求並與後端MySQL創建鏈接的網絡模塊。網關能夠用兩種模式工做,一種稱爲noshard,此模式下網關不處理/不解析SQL語句,透明轉發請求和應答。另外一種模式稱爲shard(分佈式,即支持自動水平分表)模式下,TProxy會解析SQL並轉發到不一樣的數據分片。
在實現XA以前,網關不容許在一個事務中向多個SET發送DML語句。由於未實現二階段提交(2PC)時,事務採用一階段提交,若是分佈式中某一個SET提交失敗了或回滾了,那麼這個分佈式事務就處於不一致的狀態。
(網關的工做方式)
二階段提交中須要的事務管理器(TM)。爲了解決容災、簡化架構,騰訊雲DCDB將TM實如今TProxy中,而DCDB的網關是一個無狀態的模塊,經過這一架構,DCDB XA能夠支持:
(1)、分佈式事務對業務透明,兼容單機事務語法(start transaction/commit/rollback/savepoint);
(2)、每一個網關均可以獨立接受和處理事務請求,且無需與其餘網關進行協調節點故障不丟失事務;
(3)、容許顯式事務中多條語句分別發給多個分片;
(4)、網關無需持久狀態,無需容災,能夠隨時經由調度集羣退出或加入集羣,且性能能夠擴展;
(5)、支持autocommit下單條語句寫訪問多個分片等。
DCDB網關還容許以流式處理方式運行group by、order by,流式處理讓這類操做變得很是方式很是高效;網關還支持兩個Shard使用shardkey(分表鍵)作等值鏈接,以及使用shardkey的子查詢。
將來,騰訊雲還計劃支持分佈式JOIN、Sparksql、二級分區等高級功能,兼容更多MySQL高級語法。
三、強同步與XA
因爲騰訊雲DCDB默認採用強同步複製,即主從節點數據徹底一致,所以XA事務也遵循強同步的邏輯,即需等待從機確認數據同步後,纔給業務以應答(commit)。基於強同步,在如下兩種異常狀況下,DCDB XA可輕鬆應對。
(1)、主節點故障時,已確認事務數據不會丟失:主節點故障那麼擁有最新數據和binlog的從機就被選爲主節點,這其中的數據也包括全部已經向用戶確認完成提交的事務的數據。
(2)、原主節點恢復後從新加入集羣,未確認事務自動閃回:原主節點恢復從新接入集羣,它將做爲從機運行,此時他可能存留多餘的已提交事務(此時事務並未獲得強同步同步確認,即原備機並無相關數據),那麼這些事務會被閃回。雖然這些事務可能已經在原主節點的MySQL內部完成提交,但因爲強同步機制,他並不會向客戶端返回commit語句,這意味着仍被視爲一個未完成的事務。所以,這些事務的閃回了也並無破壞數據庫的ACID屬性。這裏值得說的是,閃回flashback是基於binlog生成作逆操做,它與數據庫回滾並不一樣rollback,閃回能夠作DDL操做。
騰訊雲DCDB的強同步爲騰訊金融級數據庫自研的一項能力,性能比官方半同步大幅提升,幾乎等於異步複製性能,騰訊雲DCDB在騰訊內部應用多年,未發生過一塊兒由於主從切換、故障帶來的數據偏差。並且,從性能上,也撐住了騰訊公司各種大型運營活動如紅包、各種遊戲大型推廣等海量併發,其主要緣由是強同步採用異步提交/等待方式,且不佔用數據庫工做線程。
四、併發控制與隔離級別
爲了達到數據一致性和性能的平衡,分佈式事務的關鍵是數據庫隔離控制。XA的隔離級別最高能夠達到serializable(徹底串行化),該級別將不存在幻讀的問題,serializable級別能夠經過設置SET global tx_isolation='serializable'來對DCDB全部物理分片(和其上承載的MySQL數據庫)進行設置。固然,也能夠經過調整隔離級別以調整數據庫實例性能,理論上,Read Uncommitted性能最高,但可能存在髒讀、幻讀的狀況。
ANSI/ISO定義的SQL-92
標準定義的四種隔離級別
五、分佈式事務處理算法
前面講到,騰訊雲DCDB的網關在shard模式下已經可以解析SQL語句,騰訊雲在網關上實現TM以使得XA最具效率。爲此,咱們在網關中實現TM中的協調器(coordinator),並在網關中維護每一個XA的狀態,記錄好每一個XA寫入的SET,而後在提交階段作兩階段提交便可,大體流程以下:
(1)、網關在執行一個事務的insert/update/delete語句時,會記錄這個語句修改了哪一個SET;
(2)、SET時會發送一個XA START在這個SET上面啓動事務分支;(注:XA事務開始時,並不確認事務將以哪一種提交方式執行,所以老是以xa start來開啓一個事務);
(3)、檢測是否影響SET個數≤1,如果,則直接作一階段提交(xa commit one phase)。
(4)、影響SET個數≥2,則改成作兩階段提交:
1)網關首先發送xa prepare‘gtid’ 給參與的SET(大於等於2個SET);
2)SET接受到xa prepare應答ok(表示成功確認);
3)收到成功確認後,寫入XA對應的commit log,再發送xa commit‘gtid’參與SET;
4)若是有SET返回了錯誤,或者寫入commit log失敗,那麼網關發送 xa rollback‘gtid’給相關SET,這樣這個全局事務就實現了回滾。
騰訊雲DCDB的commit log是在SET中存儲,這個步驟是批量完成的——網關後臺線程會聚集正在提交的分佈式事務而後在獨立的鏈接和事務中完成對每一個SET的寫入,而且每一個事務的commit log只寫入一個SET中,於是這個開銷並無顯著增長事務的提交耗時或者下降TPS。並且,依賴騰訊雲DCDB已有的強同步和容災特性,只要XA成功寫入了commit log,就意味着數據已經寫入從機。
雖然絕大多數的XA事務能夠正常執行。但極少數的異常狀況仍是會影響整個集羣穩定性,所以,騰訊雲設計了agent(監控模塊),在故障後繼續協助完成本地MySQL上面prepared事務的提交,即agent會解析commit log,並根據異常處理本地仍然處於prepared的事務數據;若是commit log上面沒有事務的提交決定的話,agent也會回滾超時未被提交的prepared本地事務。
雖然在MySQL 5.五、5.6等版本早已實現XA,但這兩個版本相對於5.7仍然有性能不足,所以騰訊雲目前只在公有云上基於5.7.17支持XA版本。現在,騰訊雲在MySQL 、percona、MariaDB等分支中作了大量優化和相關bug修復(部分已經提交到社區修復patch或開源),將來騰訊雲還將繼續致力於新特性的開發和相關Bug的修復,爲衆多有須要的企業,提供更好的分佈式數據庫支持。
此文已由做者受權騰訊雲技術社區發佈,轉載請註明文章出處
原文連接:cloud.tencent.com/community/a…
獲取更多騰訊海量技術實踐乾貨,歡迎你們前往騰訊雲技術社區