分布式事務(wù)是分布式數(shù)據(jù)庫(kù)最難攻克的技術(shù)之一,分布式事務(wù)為分布式數(shù)據(jù)庫(kù)提供一致性數(shù)據(jù)訪問(wèn)的支持,保證全局讀寫原子性和隔離性,提供一體化分布式數(shù)據(jù)庫(kù)的用戶體驗(yàn)。
分布式事務(wù)是分布式數(shù)據(jù)庫(kù)最難攻克的技術(shù)之一,分布式事務(wù)為分布式數(shù)據(jù)庫(kù)提供一致性數(shù)據(jù)訪問(wèn)的支持,保證全局讀寫原子性和隔離性,提供一體化分布式數(shù)據(jù)庫(kù)的用戶體驗(yàn)。
【資料圖】
[[282776]]
本文主要分享分布式數(shù)據(jù)庫(kù)中的時(shí)鐘解決方案及分布式事務(wù)管理技術(shù)方案?;旌线壿嫊r(shí)鐘(HLC)可以實(shí)現(xiàn)本地獲取,避免了中心時(shí)鐘的性能瓶頸和單點(diǎn)故障,同時(shí)維護(hù)了跨實(shí)例的事務(wù)或事件的因果(happen before)關(guān)系。
本次的分享主要圍繞以下兩個(gè)方面:
時(shí)鐘方案分布式事務(wù)管理一、時(shí)鐘方案
1、數(shù)據(jù)庫(kù)為什么需要時(shí)鐘
數(shù)據(jù)庫(kù)歸根結(jié)底是為了將每一個(gè)事務(wù)進(jìn)行排序。在單機(jī)上情況下,事務(wù)排序可以非常簡(jiǎn)單的實(shí)現(xiàn),但是在分布式下如何進(jìn)行事務(wù)排序?
數(shù)據(jù)庫(kù)通過(guò)事務(wù)對(duì)外提供數(shù)據(jù)相關(guān)操作的ACID。數(shù)據(jù)庫(kù)對(duì)事務(wù)順序的標(biāo)識(shí)決定了事務(wù)的原子性和隔離性。原子性指一個(gè)事務(wù)是完整的,既發(fā)生或不發(fā)生,代表每個(gè)事務(wù)都是獨(dú)立的。隔離性指事務(wù)之間是相互隔離的。時(shí)鐘有各種方式來(lái)標(biāo)識(shí)一個(gè)事務(wù)的順序,如Oracle每一個(gè)日志都有日志序列號(hào)LSN,事務(wù)ID,以及時(shí)間戳。
目前許多商業(yè)和開(kāi)源數(shù)據(jù)庫(kù)產(chǎn)品都支持MVCC,MVCC通過(guò)支持?jǐn)?shù)據(jù)的多版本,允許讀寫相同數(shù)據(jù),實(shí)現(xiàn)并發(fā),在讀多寫少的場(chǎng)景下極大的提升了性能。
多版本出現(xiàn)之后,其本身就隱含了事務(wù)的順序。當(dāng)一個(gè)事務(wù)開(kāi)始之后,需要確定哪個(gè)版本的數(shù)據(jù)是可見(jiàn)的和不可見(jiàn)的,所以這就涉及到了多體系,多版本和版本回收等問(wèn)題。
一個(gè)很經(jīng)典的場(chǎng)景,淘寶或天貓的購(gòu)物場(chǎng)景,有一條商品記錄,用戶每買一個(gè)商品,就是對(duì)商品數(shù)量記錄做一次扣減。商品記錄版本會(huì)變的一個(gè)非常長(zhǎng),把所有的版本都保存起來(lái)是不合理的,否則整個(gè)存儲(chǔ)容量就不斷增加。那如何進(jìn)行版本回收?在回收的時(shí)候也需要有順序,確定應(yīng)該回收哪些版本?
2、分布式數(shù)據(jù)庫(kù)下的時(shí)鐘
分布式數(shù)據(jù)庫(kù)下的時(shí)鐘和單機(jī)數(shù)據(jù)庫(kù)下的時(shí)鐘有什么區(qū)別?
首先,單機(jī)數(shù)據(jù)庫(kù)的排序非常簡(jiǎn)單,通過(guò)日志序列號(hào)或事務(wù)ID就可以表示事務(wù)的順序。在分布式數(shù)據(jù)庫(kù)下,因?yàn)閿?shù)據(jù)庫(kù)運(yùn)行在多臺(tái)服務(wù)器上,每個(gè)數(shù)據(jù)庫(kù)實(shí)例有獨(dú)立的時(shí)鐘或日志(LSN),每一個(gè)本地的時(shí)鐘不能反映全局的順序。
服務(wù)器之間會(huì)有時(shí)鐘偏移,最理想情況是一個(gè)分布式數(shù)據(jù)庫(kù)部署100個(gè)節(jié)點(diǎn),100個(gè)節(jié)點(diǎn)的時(shí)鐘是完全同步的。但實(shí)際情況下,在機(jī)房做越軌需要做時(shí)鐘校對(duì),因?yàn)榉?wù)器和服務(wù)器之間時(shí)鐘點(diǎn)有快慢之差,所以分布式數(shù)據(jù)庫(kù)下的時(shí)鐘無(wú)法做全局設(shè)置的反映。
3、時(shí)鐘解決方案
時(shí)鐘解決方案有很多,如使用統(tǒng)一的中心節(jié)點(diǎn),或者使用獨(dú)立的服務(wù)器產(chǎn)生分布式時(shí)鐘。
還有一種解決方案是邏輯時(shí)間,Lamport時(shí)鐘是邏輯時(shí)鐘。邏輯時(shí)鐘指的是沒(méi)有任何一個(gè)中心節(jié)點(diǎn)來(lái)產(chǎn)生時(shí)間,每一個(gè)節(jié)點(diǎn)都有自己本地的邏輯時(shí)間。
比如有十個(gè)Oracle數(shù)據(jù)庫(kù),每個(gè)節(jié)點(diǎn)有自己的LSN,如果節(jié)點(diǎn)的事務(wù)比較多,事務(wù)ID跑的就比較快。如果節(jié)點(diǎn)事務(wù)比較少,事務(wù)ID就跑得比較慢。
下圖展示了目前主流的幾種時(shí)鐘解決方案,其中TIDB是國(guó)人的驕傲,TIDB使用的是中心時(shí)鐘。除此之外,Postgres-XL使用了GTM,也屬于中心時(shí)鐘。Oracle使用的是邏輯時(shí)鐘SCN。Cockraoch DB 是模仿Spanner做的分布式數(shù)據(jù)庫(kù),使用的是混合邏輯時(shí)鐘。
還有最知名的Google Cloud Spanner,Spanner對(duì)硬件依賴比較高,使用的是Truetime。Truetime本質(zhì)上是一個(gè)原子鐘,通過(guò)原子鐘授時(shí)確保兩個(gè)服務(wù)器之間時(shí)鐘偏移在很小的范圍之內(nèi)。
4、邏輯時(shí)鐘
邏輯時(shí)鐘在分布式環(huán)境下如何實(shí)現(xiàn)?如下圖,有A、B和C,3個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)會(huì)有自己的邏輯時(shí)間,邏輯時(shí)間可以簡(jiǎn)單的理解為單調(diào)遞增的自然數(shù),0、1、2、3…。事務(wù)開(kāi)始后加1,新事務(wù)開(kāi)再加1。
整個(gè)分布式環(huán)境下,三個(gè)節(jié)點(diǎn)完全獨(dú)立,相互之間沒(méi)有關(guān)系。如果事務(wù)跨多個(gè)節(jié)點(diǎn),涉及到多個(gè)節(jié)點(diǎn)交互,產(chǎn)生一個(gè)事務(wù)的時(shí)候,本地時(shí)鐘要加1。發(fā)message出去的時(shí)候,要把message的主體發(fā)出去,還要將本地的時(shí)間發(fā)給另一個(gè)節(jié)點(diǎn)。收到一個(gè)message節(jié)點(diǎn)后要處理這條消息,從收到的消息里面將對(duì)時(shí)間和本地的邏輯時(shí)間做一個(gè)取值,取最大的值設(shè)為本地時(shí)間。
如果A節(jié)點(diǎn)發(fā)布較快,第一個(gè)事務(wù)完成以后,要做第二個(gè)事務(wù),這時(shí)與B節(jié)點(diǎn)有交流,A加1,然后將時(shí)鐘帶到B節(jié)點(diǎn),B節(jié)點(diǎn)直接從0跳到2。如此就在兩個(gè)時(shí)鐘之間建立了聯(lián)系,通過(guò)建立聯(lián)系,將兩個(gè)節(jié)點(diǎn)之間的邏輯時(shí)鐘拉平,這時(shí)候就構(gòu)建它們之間的happen before的關(guān)系,代表A節(jié)點(diǎn)的事務(wù)是在B節(jié)點(diǎn)的新事務(wù)開(kāi)始之前完成的。
分布式數(shù)據(jù)庫(kù)中,如果兩個(gè)事務(wù)沒(méi)有操作同樣的節(jié)點(diǎn),則兩個(gè)事務(wù)是無(wú)關(guān)的事務(wù)。無(wú)關(guān)的事務(wù)可以認(rèn)為是沒(méi)有先后順序的。但是當(dāng)一個(gè)事務(wù)橫跨多個(gè)節(jié)點(diǎn)的時(shí)候,將多個(gè)節(jié)點(diǎn)之間的關(guān)系建立起來(lái),就變成有關(guān)系的事務(wù),構(gòu)建的是事務(wù)間的因果序。
所謂因果序,如果同樣來(lái)了兩個(gè)事務(wù),一個(gè)事務(wù)操作AB節(jié)點(diǎn),另外一個(gè)事務(wù)操作BC節(jié)點(diǎn),因?yàn)樗鼈冊(cè)贐節(jié)點(diǎn)上建立了一個(gè)聯(lián)系。通過(guò)B節(jié)點(diǎn)的關(guān)系,將事務(wù)的順序維護(hù)起來(lái)。
純邏輯時(shí)鐘可以起到因果一致性和因果序的能力。那邏輯時(shí)鐘最大的問(wèn)題是什么呢?
最極端的情況下,節(jié)點(diǎn)和節(jié)點(diǎn)之間永遠(yuǎn)不產(chǎn)生聯(lián)系,兩個(gè)節(jié)點(diǎn)之間的邏輯時(shí)鐘的差距會(huì)越來(lái)越大。這時(shí)如果兩個(gè)節(jié)點(diǎn)之間做查詢或者備份,需要強(qiáng)制將它們建立關(guān)系,那么兩節(jié)點(diǎn)之間的gap會(huì)變得非常大。
5、混合時(shí)鐘
雖然機(jī)器和機(jī)器之間物理時(shí)鐘有偏移,但如果有NTP校準(zhǔn)或者Google的Truetime這種時(shí)鐘服務(wù)器話,其物理時(shí)鐘的差距是非常小的。
混合時(shí)鐘把分布式下的時(shí)鐘切成兩部分,上半部分用物理時(shí)鐘來(lái)填充,下半部分用邏輯時(shí)鐘來(lái)填充。填充在一起變成了一個(gè)HLC時(shí)鐘,既混合邏輯時(shí)鐘。它既有物理時(shí)鐘的部分,又有邏輯時(shí)鐘的部分。由于物理時(shí)鐘服務(wù)器之間的差距不會(huì)特別大,所以可以比較物理時(shí)鐘大小。而物理時(shí)鐘又有一定的偏差,在一定的偏差范圍內(nèi),可以使用邏輯時(shí)鐘做校準(zhǔn)。
下圖是混合邏輯時(shí)鐘的一個(gè)示例。當(dāng)發(fā)送一個(gè)消息的時(shí)候,首先應(yīng)該把邏輯時(shí)鐘的物理時(shí)鐘部分與當(dāng)前的時(shí)鐘做一個(gè)比較。如果當(dāng)前的物理時(shí)鐘是4點(diǎn),新事務(wù)產(chǎn)生后,因?yàn)槲锢頃r(shí)鐘沒(méi)變,新事務(wù)加在邏輯時(shí)鐘的部分(加1)。
如果物理時(shí)鐘從4點(diǎn)變成4:01,則將物理時(shí)鐘推進(jìn)。物理時(shí)鐘如果不推進(jìn),就加邏輯時(shí)鐘。如果物理時(shí)鐘發(fā)生了變化就把物理時(shí)鐘往上推進(jìn),將邏輯時(shí)鐘部分置零。
6、HLC和中心時(shí)鐘的差別
基于中心時(shí)鐘的方案的時(shí)間是通過(guò)事務(wù)ID來(lái)判斷的,從而為所以事物排序。分布式數(shù)據(jù)庫(kù)中,需要消除中心節(jié)點(diǎn)。一種方法是純邏輯時(shí)鐘,但邏輯時(shí)鐘之間無(wú)法比較大小。另一種方法是混合邏輯時(shí)鐘(HLC),它為數(shù)據(jù)庫(kù)定義了一類因果關(guān)系的事務(wù)。
混合邏輯時(shí)鐘沒(méi)有中心節(jié)點(diǎn),用本地的物理時(shí)間加上邏輯時(shí)間。本地產(chǎn)生的事務(wù)不保序,但是如果事務(wù)跨了節(jié)點(diǎn),其因果聯(lián)系是有順序的。
如下圖T1,T2和T3代表提交時(shí)間,T1是一個(gè)分布式事務(wù),T2是一個(gè)單機(jī)事務(wù),T3是一個(gè)分布式事務(wù)。因?yàn)門1 是一個(gè)分布式事務(wù),在數(shù)據(jù)庫(kù)節(jié)點(diǎn)上進(jìn)1是比進(jìn)2先執(zhí)行,所以在整個(gè)時(shí)鐘里面,進(jìn)1小于進(jìn)2,進(jìn)1也小于進(jìn)3。通過(guò)這種方式,將有關(guān)系的事務(wù)的順序排好。
7、中心式 VS. 分布式 VS. Truetime
如下圖,中心式時(shí)鐘的優(yōu)點(diǎn)一目了然,它可以保證全局一致的時(shí)間。
分布式數(shù)據(jù)庫(kù)下的時(shí)鐘的優(yōu)點(diǎn)是無(wú)中心化的性能和無(wú)HA瓶頸,因?yàn)椴恍枰行牡氖跁r(shí)服務(wù)。分布式數(shù)據(jù)庫(kù)下的時(shí)鐘主要有兩個(gè)能力,第一個(gè)能力是可以做到計(jì)算和存儲(chǔ)的水平擴(kuò)展。
另外,因?yàn)榉植际綌?shù)據(jù)庫(kù)把一個(gè)業(yè)務(wù)的workload拆分到了不同的機(jī)器上,從而單點(diǎn)故障帶來(lái)的影響減小了。把核心數(shù)據(jù)庫(kù)拆成了幾百份,任何一個(gè)單點(diǎn)數(shù)據(jù)庫(kù)故障帶來(lái)的整個(gè)系統(tǒng)可用性的下跌是非常小的。
這說(shuō)明了為什么現(xiàn)在的分布式和互聯(lián)網(wǎng)+結(jié)合在一起比較火,一個(gè)很重要的原因是分布式降低了單點(diǎn)故障對(duì)業(yè)務(wù)帶來(lái)的的可用性的影響。
不僅僅是互聯(lián)網(wǎng)公司,包括金融類的銀行也想往分布式走,一個(gè)方面是為了解決容量和擴(kuò)展性的問(wèn)題,另外一方面也是為了解決高可用問(wèn)題。
中心式的缺點(diǎn)是會(huì)有單點(diǎn)的single point of failure。分布式時(shí)鐘雖然消除了單點(diǎn)的影響,但是時(shí)鐘是不可以排序的,無(wú)法實(shí)現(xiàn)真正的外圍一致性。外圍一致性指的是每?jī)蓚€(gè)事務(wù)都可以排序。而分布式時(shí)鐘只能對(duì)有關(guān)聯(lián)的事務(wù)進(jìn)行排序,實(shí)現(xiàn)因果順序。
Google的Truetime的優(yōu)點(diǎn)是保證全局一致時(shí)間,簡(jiǎn)化應(yīng)用開(kāi)發(fā)。缺點(diǎn)首先是需要專有的硬件,如果Truetime的原子鐘授時(shí)的話,也會(huì)有一定的時(shí)鐘偏差,這個(gè)時(shí)鐘偏差物理上無(wú)法克服。Google Spanner的paper中可以發(fā)現(xiàn)每一個(gè)事務(wù)的提交都要等待一段時(shí)間,就是要等這段時(shí)鐘偏差。
二、分布式事務(wù)管理
1、兩階段提交
分布式事務(wù)管理是為了保證全局讀寫原子性和隔離性。一個(gè)事務(wù)要跨兩個(gè)節(jié)點(diǎn),這時(shí)候存在失敗的可能性。假如一個(gè)節(jié)點(diǎn)成功一個(gè)節(jié)點(diǎn)失敗,那么看到的結(jié)果就是不一致的,這喪失了事務(wù)的原子性。
還有一種是兩個(gè)節(jié)點(diǎn)上都提交成功,但是因?yàn)閮蓚€(gè)節(jié)點(diǎn)本身的時(shí)間不一樣,導(dǎo)致提交的時(shí)間也不一樣。如果用MVCC去讀這個(gè)事務(wù),能看到一半,另一半可能看不到,這樣就無(wú)法保證事務(wù)的原子性。
對(duì)于事務(wù)的原子性問(wèn)題,目前相關(guān)技術(shù)已經(jīng)非常成熟,既兩階段提交。如果要保證一個(gè)分布式事務(wù)成功或者失敗,可以利用兩個(gè)階段提交技術(shù),先做一個(gè)prepare事務(wù),如果所有的prepare都可以,再做commit。
2、其它分布式事務(wù)管理技術(shù)
常見(jiàn)的分布式事務(wù)管理技術(shù)分為三類。
第一類是兩個(gè)階段提交技術(shù),包含prepare階段和commit階段。
第二類基于MVOCC,其中FOUNDATION DB是蘋果開(kāi)源的分布式數(shù)據(jù)庫(kù),使用的是MVOCC,可以理解為OCC(optimistic concurrency control)。OCC指在事務(wù)提交時(shí)檢查是否有沖突,基于沖突有設(shè)置沖突檢測(cè)算法和權(quán)重算法,最后選擇毀掉或者提交哪個(gè)事務(wù)。對(duì)于鎖,在事前和在更新的時(shí)候加鎖,提交的時(shí)候檢查沖突。在沖突不劇烈的情況下,因?yàn)闆](méi)有加鎖開(kāi)銷,整個(gè)吞吐非常高。在沖突劇烈的情況下,大量的abort事務(wù)會(huì)反復(fù)回滾。
第三類技術(shù)主要針對(duì)確定性事務(wù),如FAUNA技術(shù)。
美國(guó)的一位教授提出了確定性事務(wù),并基于確定性事務(wù)模型創(chuàng)辦了一家公司,創(chuàng)建了一個(gè)分布式數(shù)據(jù)庫(kù)(FAUNA)。確定性事務(wù)指事務(wù)是完整的,而不是交互型的。
比如,在淘寶這種互聯(lián)網(wǎng)企業(yè)處理的都是非確定性事務(wù)。非確定性事務(wù)只begin事務(wù),select事務(wù)等,每個(gè)操作都是交互的,既APP需要跟DataBase做交互。
如果站在數(shù)據(jù)庫(kù)的視角,數(shù)據(jù)庫(kù)永遠(yuǎn)無(wú)法預(yù)測(cè)下一條語(yǔ)句,這類事物是非確定性的。確定性事務(wù)是把一個(gè)事務(wù)所有的邏輯一次性寫好,然后發(fā)送給DataBase。DataBase收到事務(wù)的時(shí)候,清楚這個(gè)事務(wù)需要操作哪些表,讀取哪些記錄并進(jìn)行哪些操作。從數(shù)據(jù)庫(kù)的視角來(lái)說(shuō)事務(wù)是完全確定的。拿到一個(gè)確定性事務(wù),可以事先將這些事務(wù)排好序。兩個(gè)事務(wù)之間如果操作相同的記錄,就排個(gè)先后,如果不操作相同的記錄,就并發(fā)的發(fā)出去。
使用這種方式可以做到既不用加鎖,也不用在事后提交的時(shí)候做沖突檢測(cè)。但是它的要求是事務(wù)不能是交互型的。
3、HLC和兩階段提交
混合邏輯時(shí)鐘(HLC)格式如下。如果有64個(gè)字節(jié),首先預(yù)留5字節(jié)保證兼容性,在做系統(tǒng)設(shè)計(jì)的話,通常需要預(yù)留一些字節(jié)或以防出現(xiàn)一些問(wèn)題時(shí)沒(méi)東西可用。中間再留43字節(jié)做物理時(shí)鐘。后面的16字節(jié)做邏輯時(shí)鐘。如果時(shí)鐘精確到毫秒級(jí),43字節(jié)的物理時(shí)鐘意味著279年,表示數(shù)據(jù)庫(kù)不斷運(yùn)行,279年不掛,一般來(lái)說(shuō)這不太可能。
如果物理時(shí)鐘到天級(jí),一天才能變一位,那物理時(shí)鐘就失去了意義。16字節(jié)是65536,65536意味著一毫秒內(nèi)可以發(fā)起65536個(gè)事務(wù),。一般開(kāi)始和結(jié)束的時(shí)候都要消耗兩個(gè)時(shí)鐘,除以二,既一毫秒內(nèi)可處理3萬(wàn)多的事務(wù),單節(jié)點(diǎn)一秒內(nèi)可以做到3千多萬(wàn)事務(wù)。
4、HLC時(shí)鐘偏移的問(wèn)題
HLC和事務(wù)的吞吐有關(guān)系,因?yàn)樗形锢頃r(shí)鐘,能夠展示不同的節(jié)點(diǎn)之間的時(shí)鐘差。如果真的出現(xiàn)了時(shí)鐘偏移怎么辦?
下圖提供了一個(gè)簡(jiǎn)單的公式。沒(méi)有偏差的情況下,理論上節(jié)點(diǎn)可以做到3千萬(wàn)的TPS,當(dāng)然在工程上是做不到的。
如果兩個(gè)節(jié)點(diǎn)時(shí)鐘之間偏移量是5毫秒,那么在5毫秒之內(nèi)只能通過(guò)邏輯時(shí)鐘去彌補(bǔ)。如果原來(lái)6萬(wàn)個(gè)邏輯時(shí)鐘在1毫秒內(nèi)就能做完,現(xiàn)在則需要5毫秒,導(dǎo)致整個(gè)事務(wù)的吞吐下降了600萬(wàn)。所以時(shí)鐘偏移會(huì)導(dǎo)致peakTPS大幅下降。
下圖給出了幾種解決方案。比較簡(jiǎn)單的是允許設(shè)置最大時(shí)鐘偏移,如果整個(gè)機(jī)房或者集群中兩個(gè)節(jié)點(diǎn)之間最大偏移超過(guò)了100毫秒,就把該異常節(jié)點(diǎn)清除。目前來(lái)看,機(jī)房都有NTP授時(shí)服務(wù),所以發(fā)生如此大時(shí)鐘偏移的概率非常小。另一種方式是不清除異常節(jié)點(diǎn),但是可以允許邏輯時(shí)鐘overflow到物理時(shí)鐘部分,使邏輯時(shí)鐘更大,這樣可以允許更多的事務(wù)在當(dāng)前時(shí)鐘內(nèi)發(fā)生。