云計(jì)算
近日,騰訊云發(fā)布了分布式數(shù)據(jù)庫(kù)解決方案(dcdb),其最明顯的特性之一就是提供了高于開(kāi)源分布式事務(wù)xa的性能。大型業(yè)務(wù)系統(tǒng)有著用戶多、并發(fā)高的特點(diǎn),在這方面,集中式數(shù)據(jù)庫(kù)(單機(jī)數(shù)據(jù)庫(kù))的性能很難支持,因此主流的互聯(lián)網(wǎng)公司往往采用分布式(架構(gòu))數(shù)據(jù)庫(kù),物理上利用更多的低端設(shè)備,邏輯上對(duì)大表水平拆分支撐業(yè)務(wù)的需要。
雖然分布式數(shù)據(jù)庫(kù)能解決性能難題,但事務(wù)一致性(consistency)的問(wèn)題,卻很難在分布式數(shù)據(jù)庫(kù)上得到解決。
分布式事務(wù)老大難題,數(shù)據(jù)一致難以實(shí)現(xiàn)
眾所周知,一個(gè)事務(wù)所做的更新,分布式數(shù)據(jù)庫(kù)系統(tǒng)內(nèi)部多個(gè)獨(dú)立的數(shù)據(jù)節(jié)點(diǎn)完成(每個(gè)節(jié)點(diǎn)的本地事務(wù)是這個(gè)全局事務(wù)的一個(gè)事務(wù)分支),在這樣一個(gè)全局事務(wù)提交期間,有可能某些事務(wù)分支無(wú)法成功提交。
針對(duì)這一問(wèn)題,雖然業(yè)內(nèi)早已存在理論解決方案——二階段提交協(xié)議(簡(jiǎn)稱2pc),并延伸出分布式事務(wù)(簡(jiǎn)稱xa)的解決方案。但業(yè)內(nèi)卻少有工程化實(shí)現(xiàn)且大規(guī)模應(yīng)用的案例。而騰訊云分布式數(shù)據(jù)庫(kù)dcdb,卻已在內(nèi)部業(yè)務(wù)中應(yīng)用多年。
(圖:二階段提交算法)
目前dcdb已應(yīng)用在騰訊內(nèi)部90%%u4ee5上的交易、計(jì)費(fèi)業(yè)務(wù),并且三一重工(樹(shù)根互聯(lián))、匯通天下(g7)、閱文集團(tuán)(起點(diǎn)/創(chuàng)世中文網(wǎng)等)、微眾銀行、和泰人壽、威富通等都在該產(chǎn)品。
騰訊云首發(fā)分布式數(shù)據(jù)庫(kù)xa,支持mysql 5.7
騰訊云分布式數(shù)據(jù)庫(kù)dcdb,是基于騰訊金融級(jí)數(shù)據(jù)庫(kù)(公司內(nèi)部代號(hào)tdsql)云化改造而來(lái)的兼容mysql協(xié)議的分布式數(shù)據(jù)庫(kù)。現(xiàn)如今,騰訊云dcdb已經(jīng)正式在mysql 5.7(percona分支)協(xié)議上支持分布式事務(wù)xa,并已在騰訊云公有云、金融云發(fā)布供開(kāi)發(fā)者使用。開(kāi)發(fā)者可以通過(guò)申請(qǐng)dcdb實(shí)例,并在初始化后,連接實(shí)例運(yùn)行如下sql進(jìn)行初始化:
mysql> xa init;
query ok, 0 rows affected (0.03 sec)
注意:初始化xa前,請(qǐng)開(kāi)啟強(qiáng)同步復(fù)制能力,另外該sql會(huì)創(chuàng)建xa.gtid_log_t,用戶在后續(xù)使用中萬(wàn)勿對(duì)其進(jìn)行任何操作。。
為更好的支持分布式事務(wù),dcdb還新增了sql命令:
1) select gtid(),獲取當(dāng)前分布式事務(wù)的gtid(事務(wù)的全局唯一性標(biāo)識(shí)),如果該事務(wù)不是分布式事務(wù)則返回空;
gtid的格式:
‘網(wǎng)關(guān)id’-‘網(wǎng)關(guān)隨機(jī)值’-‘序列號(hào)’-‘時(shí)間戳’-‘分區(qū)號(hào)’,例如 c46535fe-b6-dd-595db6b8-25
2) select gtid_state(“gtid”),獲取“gtid”的狀態(tài),可能的結(jié)果有:
a) “commit”,標(biāo)識(shí)該事務(wù)已經(jīng)或者最終會(huì)被提交
b) “abort”,標(biāo)識(shí)該事務(wù)最終會(huì)被回滾
c) 空,由于事務(wù)的狀態(tài)會(huì)在一個(gè)小時(shí)之后清楚,因此有以下兩種可能:
1) 一個(gè)小時(shí)之后查詢,標(biāo)識(shí)事務(wù)狀態(tài)已經(jīng)清除
2) 一個(gè)小時(shí)以內(nèi)查詢,標(biāo)識(shí)事務(wù)最終會(huì)被回滾
3) 運(yùn)維命令:
xa recover:向后端set發(fā)送xa recover命令,并進(jìn)行匯總
xa lockwait:顯示當(dāng)前分布式事務(wù)的等待關(guān)系(可以使用dot命令將輸出轉(zhuǎn)化為等待關(guān)系圖)
xa show:當(dāng)前網(wǎng)關(guān)上正在運(yùn)行的分布式事務(wù)
以python為例,可以對(duì)轉(zhuǎn)賬業(yè)務(wù)進(jìn)行如下編碼:
db =
pymysql.connect(host=testhost, port=testport, user=testuser, password=testpassword,
database=testdatabase)
cursor = db.cursor()
try:
cursor.execute(begin)
#為一個(gè)賬戶bob的余額減1
query = update
t_user_balance set balance = balance – 1
where user=\\\’bob\\\’ and balance>1)
affected =
cursor.execute(query)
if affected == 0: #余額不足,回滾事務(wù)
cursor.execute(rollback)
return
#為一個(gè)賬戶john的余額加1
query = update
t_user_balance set balance = balance 1
where user=\\\’john\\\’)
cursor.execute(query)
# 為了安全起見(jiàn),建議在這里執(zhí)行‘select gtid()’獲取當(dāng)前事務(wù)的id值,便于后續(xù)跟蹤事務(wù)的執(zhí)行情況
#提交事務(wù)
cursor.execute(commit)
except pymysql.err.mysqlerror as
e:
# 發(fā)生故障,回滾事務(wù)
cursor.execute(rollback)
分布式事務(wù)的好處在于會(huì)大大降低應(yīng)用開(kāi)發(fā)難度,因?yàn)樵谀承┎恢С謝a的數(shù)據(jù)庫(kù)中,需要業(yè)務(wù)系統(tǒng)通過(guò)特殊并且巧妙的設(shè)計(jì),而非利用數(shù)據(jù)庫(kù)來(lái)解決事務(wù)中數(shù)據(jù)不一致等問(wèn)題。這種對(duì)應(yīng)用開(kāi)發(fā)者的技術(shù)水平要求很高,越是復(fù)雜的業(yè)務(wù)系統(tǒng),越會(huì)增加開(kāi)發(fā)成本和技術(shù)門檻,這是業(yè)內(nèi)大多數(shù)開(kāi)發(fā)者面對(duì)分布式數(shù)據(jù)庫(kù)時(shí),只能望而卻步的主要原因。
騰訊云dcdb xa關(guān)鍵實(shí)現(xiàn)方案
1、dcdb架構(gòu)介紹
騰訊云dcdb整個(gè)集群架構(gòu)簡(jiǎn)圖如下圖,mysql采用主從節(jié)點(diǎn)配置(也叫作主備)一套主從節(jié)點(diǎn)叫做set,在每一個(gè)set外配置網(wǎng)關(guān)(tproxy),形成一個(gè)物理分片(shard)。
dcdb后端是mysql(或其分支版本)數(shù)據(jù)庫(kù),目前騰訊云公有云發(fā)布支持xa的版本是基于mysql 5.7.17(percona分支)。
2、網(wǎng)關(guān)(tproxy)與xa
網(wǎng)關(guān)是用于接收請(qǐng)求并與后端mysql建立連接的網(wǎng)絡(luò)模塊。網(wǎng)關(guān)可以用兩種模式工作,一種稱為noshard,此模式下網(wǎng)關(guān)不處理/不解析sql語(yǔ)句,透明轉(zhuǎn)發(fā)請(qǐng)求和應(yīng)答。另一種模式稱為shard(分布式,即支持自動(dòng)水平分表)模式下,tproxy會(huì)解析sql并轉(zhuǎn)發(fā)到不同的數(shù)據(jù)分片。
在實(shí)現(xiàn)xa之前,網(wǎng)關(guān)不允許在一個(gè)事務(wù)中向多個(gè)set發(fā)送dml語(yǔ)句。因?yàn)槲磳?shí)現(xiàn)二階段提交(2pc)時(shí),事務(wù)采用一階段提交,如果分布式中某一個(gè)set提交失敗了或回滾了,那么這個(gè)分布式事務(wù)就處于不一致的狀態(tài)。
(網(wǎng)關(guān)的工作方式)
二階段提交中需要的事務(wù)管理器(tm)。為了解決容災(zāi)、簡(jiǎn)化架構(gòu),騰訊云dcdb將tm實(shí)現(xiàn)在tproxy中,而dcdb的網(wǎng)關(guān)是一個(gè)無(wú)狀態(tài)的模塊,通過(guò)這一架構(gòu),dcdb xa可以支持:
1、分布式事務(wù)對(duì)業(yè)務(wù)透明,兼容單機(jī)事務(wù)語(yǔ)法(start transaction/commit/rollback/savepoint)。
2、每個(gè)網(wǎng)關(guān)都可以獨(dú)立接受和處理事務(wù)請(qǐng)求,且無(wú)需與其他網(wǎng)關(guān)進(jìn)行協(xié)調(diào)節(jié)點(diǎn)故障不丟失事務(wù)。
3、允許顯式事務(wù)中多條語(yǔ)句分別發(fā)給多個(gè)分片。
4、網(wǎng)關(guān)無(wú)需持久狀態(tài),無(wú)需容災(zāi),可以隨時(shí)經(jīng)由調(diào)度集群退出或加入集群,且性能可以擴(kuò)展。
5、支持autocommit下單條語(yǔ)句寫訪問(wèn)多個(gè)分片等。
dcdb網(wǎng)關(guān)還允許以流式處理方式運(yùn)行g(shù)roup by、order by,流式處理讓這類操作變得非常方式非常高效;網(wǎng)關(guān)還支持兩個(gè)shard使用shardkey(分表鍵)做等值連接,以及使用shardkey的子查詢。
未來(lái),騰訊云還計(jì)劃支持分布式j(luò)oin、sparksql、二級(jí)分區(qū)等高級(jí)功能,兼容更多mysql高級(jí)語(yǔ)法。
3、強(qiáng)同步與xa
由于騰訊云dcdb默認(rèn)采用強(qiáng)同步復(fù)制,即主從節(jié)點(diǎn)數(shù)據(jù)完全一致,因此xa事務(wù)也遵循強(qiáng)同步的邏輯,即需等待從機(jī)確認(rèn)數(shù)據(jù)同步后,才給業(yè)務(wù)以應(yīng)答(commit)。基于強(qiáng)同步,在以下兩種異常情況下,dcdb xa可輕松應(yīng)對(duì)
1、主節(jié)點(diǎn)故障時(shí),已確認(rèn)事務(wù)數(shù)據(jù)不會(huì)丟失:主節(jié)點(diǎn)故障那么擁有最新數(shù)據(jù)和binlog的從機(jī)就被選為主節(jié)點(diǎn),這其中的數(shù)據(jù)也包括所有已經(jīng)向用戶確認(rèn)完成提交的事務(wù)的數(shù)據(jù)。
2、原主節(jié)點(diǎn)恢復(fù)后重新加入集群,未確認(rèn)事務(wù)自動(dòng)閃回:原主節(jié)點(diǎn)恢復(fù)重新接入集群,它將作為從機(jī)運(yùn)行,此時(shí)他可能存留多余的已提交事務(wù)(此時(shí)事務(wù)并未得到強(qiáng)同步同步確認(rèn),即原備機(jī)并沒(méi)有相關(guān)數(shù)據(jù)),那么這些事務(wù)會(huì)被閃回。雖然這些事務(wù)可能已經(jīng)在原主節(jié)點(diǎn)的mysql內(nèi)部完成提交,但由于強(qiáng)同步機(jī)制,他并不會(huì)向客戶端返回commit語(yǔ)句,這意味著仍被視為一個(gè)未完成的事務(wù)。因此,這些事務(wù)的閃回了也并沒(méi)有破壞數(shù)據(jù)庫(kù)的acid屬性。這里值得說(shuō)的是,閃回flashback是基于binlog生成做逆操作,它與數(shù)據(jù)庫(kù)回滾并不同rollback,閃回可以做ddl操作。
騰訊云dcdb的強(qiáng)同步為騰訊金融級(jí)數(shù)據(jù)庫(kù)自研的一項(xiàng)能力,性能比官方半同步大幅提高,幾乎等于異步復(fù)制性能,騰訊云dcdb在騰訊內(nèi)部應(yīng)用多年,未發(fā)生過(guò)一起因?yàn)橹鲝那袚Q、故障帶來(lái)的數(shù)據(jù)誤差。而且