前言
首先,我想有些朋友在看到這個(gè)標(biāo)題之后可能會(huì)問:
什么是“全局一致性快照”?
它在OceanBase數(shù)據(jù)庫里起什么作用?
為什么OceanBase數(shù)據(jù)庫要在2.0版本中引入這個(gè)東西?
實(shí)際上,故事起源于數(shù)據(jù)庫中的兩個(gè)傳統(tǒng)概念:“快照隔離級(jí)別(Snapshot Isolation)”和“多版本并發(fā)控制(Multi-VersionConcurrency Control,簡(jiǎn)稱MVCC)”。這兩種技術(shù)的大致含義是:為數(shù)據(jù)庫中的數(shù)據(jù)維護(hù)多個(gè)版本號(hào)(即多個(gè)快照),當(dāng)數(shù)據(jù)被修改的時(shí)候,可以利用不同的版本號(hào)區(qū)分出正在被修改的內(nèi)容和修改之前的內(nèi)容,以此實(shí)現(xiàn)對(duì)同一份數(shù)據(jù)的多個(gè)版本做并發(fā)訪問,避免了經(jīng)典實(shí)現(xiàn)中“鎖”機(jī)制引發(fā)的讀寫沖突問題。
因此,這兩種技術(shù)被很多數(shù)據(jù)庫產(chǎn)品(如Oracle、SQL Server、MySQL、PostgreSQL)所采用,而OceanBase數(shù)據(jù)庫也同樣采用了這兩種技術(shù)以提高并發(fā)場(chǎng)景下的執(zhí)行效率。但和傳統(tǒng)的數(shù)據(jù)庫的單點(diǎn)全共享(即Shared-Everything)架構(gòu)不同,OceanBase是一個(gè)原生的分布式架構(gòu),采用了多點(diǎn)無共享(即Shared-Nothing)的架構(gòu),在實(shí)現(xiàn)全局(跨機(jī)器)一致的快照隔離級(jí)別和多版本并發(fā)控制時(shí)會(huì)面臨分布式架構(gòu)所帶來的技術(shù)挑戰(zhàn)(后文會(huì)有詳述)。
為了應(yīng)對(duì)這些挑戰(zhàn),OceanBase數(shù)據(jù)庫在2.0版本中引入了“全局一致性快照”技術(shù)。本文會(huì)介紹和OceanBase“全局一致性快照”技術(shù)相關(guān)的概念以及基本實(shí)現(xiàn)原理。
(注:本文中的所有描述,都是針對(duì)采用了“快照隔離級(jí)別”和“多版本并發(fā)控制”技術(shù)的實(shí)現(xiàn)機(jī)制。對(duì)于使用“鎖”機(jī)制來實(shí)現(xiàn)傳統(tǒng)“隔離級(jí)別(Isolation Level)”的經(jīng)典模式,不在本文的討論范圍之內(nèi)。)
傳統(tǒng)數(shù)據(jù)庫的實(shí)現(xiàn)原理
首先,我們來看一下傳統(tǒng)數(shù)據(jù)庫中是如何實(shí)現(xiàn)“快照隔離級(jí)別”和“多版本并發(fā)控制”的。
以經(jīng)典的Oracle數(shù)據(jù)庫為例,當(dāng)數(shù)據(jù)的更改在數(shù)據(jù)庫中被提交的時(shí)候,Oracle會(huì)為它分配一個(gè)“System Change Number(SCN)”作為版本號(hào)。SCN是一個(gè)和系統(tǒng)時(shí)鐘強(qiáng)相關(guān)的值,可以簡(jiǎn)單理解為等同于系統(tǒng)時(shí)間戳,不同的SCN代表了數(shù)據(jù)在不同時(shí)間點(diǎn)的“已提交版本(Committed Version)”,由此實(shí)現(xiàn)了數(shù)據(jù)的快照隔離級(jí)別。
假設(shè)一條記錄最初插入時(shí)對(duì)應(yīng)的版本號(hào)為SCN0,當(dāng)事務(wù)T1正在更改此記錄但還未提交的時(shí)候(注意:此時(shí)T1對(duì)應(yīng)的SCN1尚未生成,需要等到T1的commit階段),Oracle會(huì)將數(shù)據(jù)更改之前的已提交版本SCN0放到“回滾段(Undo Segments)”中保存起來,此時(shí)如果有另外一個(gè)并發(fā)事務(wù)T2要讀取這條記錄,Oracle會(huì)根據(jù)當(dāng)前系統(tǒng)時(shí)間戳分配一個(gè)SCN2給T2,并按照兩個(gè)條件去尋找數(shù)據(jù):
1)必須是已提交(Committed)的數(shù)據(jù);
2)數(shù)據(jù)的已提交版本(Committed Version)是小于等于SCN2的最大值。
根據(jù)上面的條件,事務(wù)T2會(huì)從回滾段中獲取到SCN0版本所對(duì)應(yīng)的數(shù)據(jù),并不理會(huì)正在同一條記錄上進(jìn)行修改的事務(wù)T1。利用這種方法,既避免了“臟讀(Dirty Read)”的發(fā)生,也不會(huì)導(dǎo)致并發(fā)的讀/寫操作之間產(chǎn)生鎖沖突,實(shí)現(xiàn)了數(shù)據(jù)的多版本并發(fā)控制。整個(gè)過程如下圖所示:

關(guān)于“快照隔離級(jí)別”和“多版本并發(fā)控制”,不同數(shù)據(jù)庫產(chǎn)品的實(shí)現(xiàn)機(jī)制會(huì)有差異,但大多遵循以下原則:
每次數(shù)據(jù)的更改被提交時(shí),都會(huì)為數(shù)據(jù)分配一個(gè)新的版本號(hào)。
版本號(hào)的變化必須保證“單調(diào)向前”。
版本號(hào)取自系統(tǒng)時(shí)鐘里的當(dāng)前時(shí)間戳,或者是一個(gè)和當(dāng)前時(shí)間戳強(qiáng)相關(guān)的值。
查詢數(shù)據(jù)時(shí),也需要一個(gè)最新版本號(hào)(同理,為當(dāng)前時(shí)間戳或者和當(dāng)前時(shí)間戳強(qiáng)相關(guān)的值),并查找小于等于這個(gè)版本號(hào)的最近已提交數(shù)據(jù)。
分布式數(shù)據(jù)庫面臨的挑戰(zhàn)
前面關(guān)于“多版本并發(fā)控制”的描述看上去很完美,但是這里面卻有一個(gè)隱含的前提條件:數(shù)據(jù)庫中版本號(hào)的變化順序必須和真實(shí)世界中事務(wù)發(fā)生的時(shí)間順序保持一致,即:
— 真實(shí)世界中較早發(fā)生的事務(wù)必然獲取更?。ɑ蛘呦嗟龋┑陌姹咎?hào);
— 真實(shí)世界中較晚發(fā)生的事務(wù)必然獲取更大(或者相等)的版本號(hào)。
如果不能滿足這個(gè)一致性,會(huì)導(dǎo)致什么結(jié)果呢?以下面的場(chǎng)景為例:
1)記錄R1首先在事務(wù)T1里被插入并提交,對(duì)應(yīng)的SCN1是10010;
2)隨后,記錄R2在事務(wù)T2里被插入并提交,對(duì)應(yīng)的SCN2是10030;
3)隨后,事務(wù)T3要讀取這兩條數(shù)據(jù),它獲取的SCN3為10020,因此它只獲取到記錄R1(SCN1SCN3,不滿足條件)。示意圖如下:

這對(duì)應(yīng)用來說就是一個(gè)邏輯錯(cuò)誤:我明明向數(shù)據(jù)庫中插入了兩條記錄并且都提成功提交了,但卻只能讀到其中的一條記錄。導(dǎo)致這個(gè)問題的原因,就是這個(gè)場(chǎng)景違反了上面所說的一致性,即SCN(版本號(hào))的變化順序沒有和真實(shí)世界中事務(wù)發(fā)生的時(shí)間順序保持一致。
其實(shí),違反了這種一致性還可能引發(fā)更極端的情況,考慮下面的場(chǎng)景:
1)記錄R1首先在事務(wù)T1里被插入并提交,對(duì)應(yīng)的SCN1是10030;
2)隨后,記錄R2在事務(wù)T2里被插入并提交,對(duì)應(yīng)的SCN2是10010;
3)隨后,事務(wù)T3要讀取這兩條數(shù)據(jù),它獲取的SCN3為10020,因此它只能獲取到記錄R2(SCN2SCN3,不滿足條件)。示意圖如下:

對(duì)于應(yīng)用來說,這種結(jié)果從邏輯上講更加難以理解:先插入的數(shù)據(jù)查不到,后插入的數(shù)據(jù)反而能查到,完全不合理。
有的朋友可能會(huì)說:上面這些情況在實(shí)際中是不會(huì)發(fā)生的,因?yàn)橄到y(tǒng)時(shí)間戳永遠(yuǎn)是單調(diào)向前的,因此真實(shí)世界中先提交的事務(wù)一定有更小的版本號(hào)。是的,對(duì)于傳統(tǒng)數(shù)據(jù)庫來說,由于采用單點(diǎn)全共享(Shared-Everything)架構(gòu),數(shù)據(jù)庫只有一個(gè)系統(tǒng)時(shí)鐘來源,因此時(shí)間戳(即版本號(hào))的變化的確能做到單調(diào)向前,并且一定和真實(shí)世界的時(shí)間順序保持一致。
但對(duì)于OceanBase這樣的分布式數(shù)據(jù)庫來說,由于采用無共享(Shared-Nothing)架構(gòu),數(shù)據(jù)分布和事務(wù)處理會(huì)涉及不同的物理機(jī)器,而多臺(tái)物理機(jī)器之間的系統(tǒng)時(shí)鐘不可避免存在差異,如果以本地系統(tǒng)時(shí)間戳作為版本號(hào),則無法保證不同機(jī)器上獲取的版本號(hào)和真實(shí)世界的時(shí)間序保持一致。還是以上面的兩個(gè)場(chǎng)景為例,如果T1、T2和T3分別在不同的物理機(jī)器上執(zhí)行,并且它們都分別以本地的系統(tǒng)時(shí)間戳作為版本號(hào),那么由于機(jī)器間的時(shí)鐘差異,完全可能發(fā)生上面所說的兩種異常。
為了解決上面所說的問題,在分布式領(lǐng)域引入了兩個(gè)概念: “外部一致性(External Consistency)” 和 “因果一致性(Causal Consistency)”。還是以上面的兩個(gè)場(chǎng)景為例,真實(shí)世界中事務(wù)的發(fā)生順序?yàn)門1 -> T2-> T3,如果SCN的變化能保證SCN1 < SCN2 < SCN3的順序,并且可以完全不關(guān)心事務(wù)發(fā)生時(shí)所在的物理機(jī)器,則認(rèn)為SCN的變化滿足了“外部一致性”。
而“因果一致性”則是“外部一致性”的一種特殊情況:事務(wù)的發(fā)生不僅有前后順序,還要有前后關(guān)聯(lián)的因果關(guān)系。因此“外部一致性”的覆蓋范圍更廣,“因果一致性”只是其中的一種情況,如果滿足了“外部一致性”則一定能滿足“因果一致性”。OceanBase在實(shí)現(xiàn)中滿足了“外部一致性”,同時(shí)也就滿足了“因果一致性”,本文后半段的內(nèi)容也主要針對(duì)“外部一致性”來展開。
業(yè)內(nèi)常用的解決方案
那么,分布式數(shù)據(jù)庫應(yīng)如何在全局(跨機(jī)器)范圍內(nèi)保證外部一致性,進(jìn)而實(shí)現(xiàn)全局一致的快照隔離級(jí)別和多版本并發(fā)控制呢?大體來說,業(yè)界有兩種實(shí)現(xiàn)方式:
1)利用特殊的硬件設(shè)備,如GPS和原子鐘(Atomic Clock),使多臺(tái)機(jī)器間的系統(tǒng)時(shí)鐘保持高度一致,誤差小到應(yīng)用完全無法感知的程度。在這種情況下,就可以繼續(xù)利用本地系統(tǒng)時(shí)間戳作為版本號(hào),同時(shí)也能滿足全局范圍內(nèi)的外部一致性。
2)版本號(hào)不再依賴各個(gè)機(jī)器自己的本地系統(tǒng)時(shí)鐘,所有的數(shù)據(jù)庫事務(wù)通過集中式的服務(wù)獲取全局一致的版本號(hào),由這個(gè)服務(wù)來保證版本號(hào)的單調(diào)向前。這樣一來,分布式架構(gòu)下獲取版本號(hào)的邏輯模型和單點(diǎn)架構(gòu)下的邏輯模型就一樣了,徹底消除了機(jī)器之間時(shí)鐘差異的因素。
第一種方式的典型代表是Google的Spanner數(shù)據(jù)庫。它使用GPS系統(tǒng)在全球的多個(gè)機(jī)房之間保持時(shí)間同步,并使用原子鐘確保本地系統(tǒng)時(shí)鐘的誤差一直維持在很小的范圍內(nèi),這樣就能保證全球多個(gè)機(jī)房的系統(tǒng)時(shí)鐘能夠在一個(gè)很高的精度內(nèi)保持一致,這種技術(shù)在Spanner數(shù)據(jù)庫內(nèi)被稱為TrueTime。在此基礎(chǔ)上,Spanner數(shù)據(jù)庫就可以沿用傳統(tǒng)的方式,以本地系統(tǒng)時(shí)間戳作為版本號(hào),而不用擔(dān)心破壞全局范圍內(nèi)的外部一致性。
這種方式的好處,是軟件的實(shí)現(xiàn)比較簡(jiǎn)單,并且避免了采用集中式的服務(wù)可能會(huì)導(dǎo)致的性能瓶頸。但這種方式也有它的缺點(diǎn),首先對(duì)機(jī)房的硬件要求明顯提高,其次“GPS+原子鐘”的方式也不能100%保證多個(gè)機(jī)器之間的系統(tǒng)時(shí)鐘完全一致,如果GPS或者原子鐘的硬件偏差導(dǎo)致時(shí)間誤差過大,還是會(huì)出現(xiàn)外部一致性被破壞的問題。根據(jù)GoogleSpanner論文中的描述,發(fā)生時(shí)鐘偏差(clock drift)的概率極小,但并不為0。下圖是Google Spanner論文中對(duì)上千臺(tái)機(jī)器所做的關(guān)于時(shí)鐘誤差范圍的統(tǒng)計(jì):

OceanBase則選用了第二種實(shí)現(xiàn)方式,即用集中式的服務(wù)來提供全局統(tǒng)一的版本號(hào)。做這個(gè)選擇主要是基于以下考慮:
可以從邏輯上消除機(jī)器間的時(shí)鐘差異因素,從而徹底避免這個(gè)問題。
避免了對(duì)特殊硬件的強(qiáng)依賴。這對(duì)于一個(gè)通用數(shù)據(jù)庫產(chǎn)品來說尤其重要,我們不能假設(shè)所有使用OceanBase數(shù)據(jù)庫的用戶都在機(jī)房里部署了“GPS+原子鐘”的設(shè)備。
OceanBase的“全局一致性快照”技術(shù)
如前文所述, OceanBase數(shù)據(jù)庫是利用一個(gè)集中式服務(wù)來提供全局一致的版本號(hào)。事務(wù)在修改數(shù)據(jù)或者查詢數(shù)據(jù)的時(shí)候,無論請(qǐng)求源自哪臺(tái)物理機(jī)器,都會(huì)從這個(gè)集中式的服務(wù)處獲取版本號(hào),OceanBase則保證所有的版本號(hào)單調(diào)向前并且和真實(shí)世界的時(shí)間順序保持一致。
有了這樣全局一致的版本號(hào),OceanBase就能根據(jù)版本號(hào)對(duì)全局(跨機(jī)器)范圍內(nèi)的數(shù)據(jù)做一致性快照,因此我們把這個(gè)技術(shù)命名為“全局一致性快照”。有了全局一致性快照技術(shù),就能實(shí)現(xiàn)全局范圍內(nèi)一致的快照隔離級(jí)別和多版本并發(fā)控制,而不用擔(dān)心發(fā)生外部一致性被破壞的情況。
但是,相信有些朋友看到這里就會(huì)產(chǎn)生疑問了,比如:
這個(gè)集中式服務(wù)里是如何生成統(tǒng)一版本號(hào)的?怎么能保證單調(diào)向前?
這個(gè)集中式服務(wù)的服務(wù)范圍有多大?整個(gè)OceanBase集群里的事務(wù)都使用同一個(gè)服務(wù)嗎?
這個(gè)集中式服務(wù)的性能如何?尤其在高并發(fā)訪問的情況下,是否會(huì)成為性能瓶頸?
如果這個(gè)集中式服務(wù)發(fā)生中斷怎么辦?
如果在事務(wù)獲取全局版本號(hào)的過程中,發(fā)生了網(wǎng)絡(luò)異常(比如瞬時(shí)網(wǎng)絡(luò)抖動(dòng)),是否會(huì)破壞“外部一致性”?
下面針對(duì)這些疑問逐一為大家解答。
首先,這個(gè)集中式服務(wù)所產(chǎn)生的版本號(hào)就是本地的系統(tǒng)時(shí)間戳,只不過它的服務(wù)對(duì)象不再只是本地事務(wù),而是全局范圍內(nèi)的所有事務(wù),因此在OceanBase中這個(gè)服務(wù)被稱作“全局時(shí)間戳服務(wù)(Global Timestamp Service,簡(jiǎn)稱GTS)”。由于GTS服務(wù)是集中式的,只從一個(gè)系統(tǒng)時(shí)鐘里獲取時(shí)間戳,因此能保證獲取的時(shí)間戳(即版本號(hào))一定是單調(diào)向前的,并且一定和真實(shí)世界的時(shí)間順序保持一致。
那么,是否一個(gè)OceanBase數(shù)據(jù)庫集群中只有一個(gè)GTS服務(wù),集群中所有的事務(wù)都從這里獲取時(shí)間戳呢?對(duì)OceanBase數(shù)據(jù)庫有了解的朋友都知道,“租戶”是OceanBase中實(shí)現(xiàn)資源隔離的一個(gè)基本單元,比較類似傳統(tǒng)數(shù)據(jù)庫中“實(shí)例”的概念,不同租戶之間的數(shù)據(jù)完全隔離,沒有一致性要求,也無需實(shí)現(xiàn)跨租戶的全局一致性版本號(hào),因此OceanBase數(shù)據(jù)庫集群中的每一個(gè)“租戶”都有一個(gè)單獨(dú)的GTS服務(wù)。這樣做不但使GTS服務(wù)的管理更加靈活(以租戶為單位),而且也將集群內(nèi)的版本號(hào)請(qǐng)求分流到了多個(gè)GTS服務(wù)中,大大減少了因單點(diǎn)服務(wù)導(dǎo)致性能瓶頸的可能。下面是OceanBase數(shù)據(jù)庫集群中GTS服務(wù)的簡(jiǎn)單示意圖:

說到性能,經(jīng)過實(shí)測(cè),單個(gè)GTS服務(wù)能夠在1秒鐘內(nèi)響應(yīng)2百萬次申請(qǐng)時(shí)間戳(即版本號(hào))的請(qǐng)求,因此只要租戶內(nèi)的QPS不超過2百萬,就不會(huì)遇到GTS的性能瓶頸,實(shí)際業(yè)務(wù)則很難觸及這個(gè)上限。雖然GTS的性能不是一個(gè)問題,但從GTS獲取時(shí)間戳畢竟比獲取本地時(shí)間戳有更多的開銷,至少網(wǎng)絡(luò)的時(shí)延是無法避免的,對(duì)此我們也做過實(shí)測(cè),在滿負(fù)荷壓測(cè)并且網(wǎng)絡(luò)正常的情況下,和采用本地時(shí)間戳做版本號(hào)相比,采用GTS對(duì)性能所帶來的影響不超過5%,絕大多數(shù)應(yīng)用對(duì)此都不會(huì)有感知。
除了保證GTS的正常處理性能之外,OceanBase數(shù)據(jù)庫還在不影響外部一致性的前提下,對(duì)事務(wù)獲取GTS的流程做了優(yōu)化,比如:
將某些GTS請(qǐng)求轉(zhuǎn)化為本地請(qǐng)求,從本地控制文件中獲取版本號(hào),避免了網(wǎng)絡(luò)傳輸?shù)拈_銷;
將多個(gè)GTS請(qǐng)求合并為一個(gè)批量請(qǐng)求,以提高GTS的全局吞吐量;
利用“本地GTS緩存技術(shù)”,減少GTS請(qǐng)求的次數(shù)。
這些優(yōu)化措施進(jìn)一步提高了GTS的處理效率,因此,使用者完全不用擔(dān)心GTS的性能。
前面所說的都是正常情況,但對(duì)于集中式服務(wù)來說一定要考慮異常情況。首先就是高可用的問題,GTS服務(wù)也像OceanBase中基本的數(shù)據(jù)服務(wù)一樣,以Paxos協(xié)議實(shí)現(xiàn)了高可用,如果GTS服務(wù)由于異常情況(比如宕機(jī))而中斷,那么OceanBase會(huì)根據(jù)Paxos協(xié)議自動(dòng)選出一個(gè)新的服務(wù)節(jié)點(diǎn),整個(gè)過程自動(dòng)而且迅速(1~15秒),無需人工干預(yù)。

那如果發(fā)生網(wǎng)絡(luò)異常怎么辦?比如網(wǎng)絡(luò)抖動(dòng)了10秒鐘,會(huì)不會(huì)影響版本號(hào)的全局一致性,并進(jìn)而影響外部一致性?對(duì)數(shù)據(jù)庫來說,外部一致性反映的是真實(shí)世界中“完整事務(wù)”之間的前后順序,比如前面說到的T1 -> T2-> T3,其實(shí)準(zhǔn)確來說是T1's Begin-> T1's End -> T2's Begin -> T2's End -> T3's Begin -> T3's End,即任意兩個(gè)完整的事務(wù)窗口之間沒有任何重疊。如果發(fā)生了重疊,則事務(wù)之間并不具備真正的“先后順序”,外部一致性也就無從談起。
因此,不管網(wǎng)絡(luò)如何異常,只要真實(shí)世界中的“完整事務(wù)”滿足這種前后順序,全局版本號(hào)就一定會(huì)滿足外部一致性。
最后,如果事務(wù)發(fā)現(xiàn)GTS響應(yīng)過慢,會(huì)重新發(fā)送GTS請(qǐng)求,以避免由于特殊情況(如網(wǎng)絡(luò)丟包)而導(dǎo)致事務(wù)的處理被GTS請(qǐng)求卡住。
總之,GTS在設(shè)計(jì)和開發(fā)的過程中已經(jīng)考慮到了諸多異常情況的處理,確??梢蕴峁┓€(wěn)定可靠的服務(wù)。
總結(jié)
有了“全局一致性快照”技術(shù)之后,OceanBase數(shù)據(jù)庫便具備了在全局(跨機(jī)器)范圍內(nèi)實(shí)現(xiàn)“快照隔離級(jí)別”和“多版本并發(fā)控制”的能力,可以在全局范圍內(nèi)保證“外部一致性”,并在此基礎(chǔ)之上實(shí)現(xiàn)眾多涉及全局?jǐn)?shù)據(jù)一致性的功能,比如全局一致性讀、全局索引等。
這樣一來,和傳統(tǒng)單點(diǎn)數(shù)據(jù)庫相比,OceanBase在保留分布式架構(gòu)優(yōu)勢(shì)的同時(shí),在全局?jǐn)?shù)據(jù)一致性上也沒有任何降級(jí),應(yīng)用開發(fā)者就可以像使用單點(diǎn)數(shù)據(jù)庫一樣使用OceanBase,完全不必?fù)?dān)心機(jī)器之間的底層數(shù)據(jù)一致性問題??梢哉f,借助“全局一致性快照”技術(shù),OceanBase數(shù)據(jù)庫完美地實(shí)現(xiàn)了分布式架構(gòu)下的全局?jǐn)?shù)據(jù)一致性!
如果想學(xué)習(xí)Java工程化、高性能及分布式、深入淺出。性能調(diào)優(yōu)、Spring,MyBatis,Netty源碼分析的朋友可以加我的Java高級(jí)架構(gòu)進(jìn)階群:180705916,群里有阿里大牛直播講解技術(shù),以及Java大型互聯(lián)網(wǎng)技術(shù)的視頻免費(fèi)分享給大家