天天看點

2.0解析系列 | 如何在分布式架構下完美實作“全局資料一緻性”?前言傳統資料庫的實作原理分布式資料庫面臨的挑戰業内常用的解決方案OceanBase的“全局一緻性快照”技術總結

OB君:本文是 “OceanBase 2.0 技術解析系列” 的第五篇文章。今天我們繼續來聊分布式架構,說說2.0中大家都很關心的“全局一緻性快照”功能。更多精彩歡迎關注OceanBase公衆号持續訂閱本系列内容!

前言

首先,我想有些朋友在看到這個标題之後可能會問:

  • 什麼是“全局一緻性快照”?
  • 它在OceanBase資料庫裡起什麼作用?
  • 為什麼OceanBase資料庫要在2.0版本中引入這個東西?

實際上,故事起源于資料庫中的兩個傳統概念:“快照隔離級别(Snapshot Isolation)”和“多版本并發控制(Multi-VersionConcurrency Control,簡稱MVCC)”。這兩種技術的大緻含義是:為資料庫中的資料維護多個版本号(即多個快照),當資料被修改的時候,可以利用不同的版本号區分出正在被修改的内容和修改之前的内容,以此實作對同一份資料的多個版本做并發通路,避免了經典實作中“鎖”機制引發的讀寫沖突問題。

是以,這兩種技術被很多資料庫産品(如Oracle、SQL Server、MySQL、PostgreSQL)所采用,而OceanBase資料庫也同樣采用了這兩種技術以提高并發場景下的執行效率。但和傳統的資料庫的單點全共享(即Shared-Everything)架構不同,OceanBase是一個原生的分布式架構,采用了多點無共享(即Shared-Nothing)的架構,在實作全局(跨機器)一緻的快照隔離級别和多版本并發控制時會面臨分布式架構所帶來的技術挑戰(後文會有詳述)。

為了應對這些挑戰,OceanBase資料庫在2.0版本中引入了“全局一緻性快照”技術。本文會介紹和OceanBase“全局一緻性快照”技術相關的概念以及基本實作原理。

(注:本文中的所有描述,都是針對采用了“快照隔離級别”和“多版本并發控制”技術的實作機制。對于使用“鎖”機制來實作傳統“隔離級别(Isolation Level)”的經典模式,不在本文的讨論範圍之内。)

傳統資料庫的實作原理

首先,我們來看一下傳統資料庫中是如何實作“快照隔離級别”和“多版本并發控制”的。

以經典的Oracle資料庫為例,當資料的更改在資料庫中被送出的時候,Oracle會為它配置設定一個“System Change Number(SCN)”作為版本号。SCN是一個和系統時鐘強相關的值,可以簡單了解為等同于系統時間戳,不同的SCN代表了資料在不同時間點的“已送出版本(Committed Version)”,由此實作了資料的快照隔離級别。

假設一條記錄最初插入時對應的版本号為SCN0,當事務T1正在更改此記錄但還未送出的時候(注意:此時T1對應的SCN1尚未生成,需要等到T1的commit階段),Oracle會将資料更改之前的已送出版本SCN0放到“復原段(Undo Segments)”中儲存起來,此時如果有另外一個并發事務T2要讀取這條記錄,Oracle會根據目前系統時間戳配置設定一個SCN2給T2,并按照兩個條件去尋找資料:

1)必須是已送出(Committed)的資料;

2)資料的已送出版本(Committed Version)是小于等于SCN2的最大值。

根據上面的條件,事務T2會從復原段中擷取到SCN0版本所對應的資料,并不理會正在同一條記錄上進行修改的事務T1。利用這種方法,既避免了“髒讀(Dirty Read)”的發生,也不會導緻并發的讀/寫操作之間産生鎖沖突,實作了資料的多版本并發控制。整個過程如下圖所示:

2.0解析系列 | 如何在分布式架構下完美實作“全局資料一緻性”?前言傳統資料庫的實作原理分布式資料庫面臨的挑戰業内常用的解決方案OceanBase的“全局一緻性快照”技術總結

關于“快照隔離級别”和“多版本并發控制”,不同資料庫産品的實作機制會有差異,但大多遵循以下原則:

  • 每次資料的更改被送出時,都會為資料配置設定一個新的版本号。
  • 版本号的變化必須保證“單調向前”。
  • 版本号取自系統時鐘裡的目前時間戳,或者是一個和目前時間戳強相關的值。
  • 查詢資料時,也需要一個最新版本号(同理,為目前時間戳或者和目前時間戳強相關的值),并查找小于等于這個版本号的最近已送出資料。

分布式資料庫面臨的挑戰

前面關于“多版本并發控制”的描述看上去很完美,但是這裡面卻有一個隐含的前提條件:資料庫中版本号的變化順序必須和真實世界中事務發生的時間順序保持一緻,即:

— 真實世界中較早發生的事務必然擷取更小(或者相等)的版本号;

— 真實世界中較晚發生的事務必然擷取更大(或者相等)的版本号。

如果不能滿足這個一緻性,會導緻什麼結果呢?以下面的場景為例:

1)記錄R1首先在事務T1裡被插入并送出,對應的SCN1是10010;

2)随後,記錄R2在事務T2裡被插入并送出,對應的SCN2是10030;

3)随後,事務T3要讀取這兩條資料,它擷取的SCN3為10020,是以它隻擷取到記錄R1(SCN1SCN3,不滿足條件)。示意圖如下:

2.0解析系列 | 如何在分布式架構下完美實作“全局資料一緻性”?前言傳統資料庫的實作原理分布式資料庫面臨的挑戰業内常用的解決方案OceanBase的“全局一緻性快照”技術總結

這對應用來說就是一個邏輯錯誤:我明明向資料庫中插入了兩條記錄并且都提成功送出了,但卻隻能讀到其中的一條記錄。導緻這個問題的原因,就是這個場景違反了上面所說的一緻性,即SCN(版本号)的變化順序沒有和真實世界中事務發生的時間順序保持一緻。

其實,違反了這種一緻性還可能引發更極端的情況,考慮下面的場景:

1)記錄R1首先在事務T1裡被插入并送出,對應的SCN1是10030;

2)随後,記錄R2在事務T2裡被插入并送出,對應的SCN2是10010;

3)随後,事務T3要讀取這兩條資料,它擷取的SCN3為10020,是以它隻能擷取到記錄R2(SCN2SCN3,不滿足條件)。示意圖如下:

2.0解析系列 | 如何在分布式架構下完美實作“全局資料一緻性”?前言傳統資料庫的實作原理分布式資料庫面臨的挑戰業内常用的解決方案OceanBase的“全局一緻性快照”技術總結

對于應用來說,這種結果從邏輯上講更加難以了解:先插入的資料查不到,後插入的資料反而能查到,完全不合理。

有的朋友可能會說:上面這些情況在實際中是不會發生的,因為系統時間戳永遠是單調向前的,是以真實世界中先送出的事務一定有更小的版本号。是的,對于傳統資料庫來說,由于采用單點全共享(Shared-Everything)架構,資料庫隻有一個系統時鐘來源,是以時間戳(即版本号)的變化的确能做到單調向前,并且一定和真實世界的時間順序保持一緻。

但對于OceanBase這樣的分布式資料庫來說,由于采用無共享(Shared-Nothing)架構,資料分布和事務處理會涉及不同的實體機器,而多台實體機器之間的系統時鐘不可避免存在差異,如果以本地系統時間戳作為版本号,則無法保證不同機器上擷取的版本号和真實世界的時間序保持一緻。還是以上面的兩個場景為例,如果T1、T2和T3分别在不同的實體機器上執行,并且它們都分别以本地的系統時間戳作為版本号,那麼由于機器間的時鐘差異,完全可能發生上面所說的兩種異常。

為了解決上面所說的問題,在分布式領域引入了兩個概念: “外部一緻性(External Consistency)” 和 “因果一緻性(Causal Consistency)”。還是以上面的兩個場景為例,真實世界中事務的發生順序為T1 -> T2-> T3,如果SCN的變化能保證SCN1 < SCN2 < SCN3的順序,并且可以完全不關心事務發生時所在的實體機器,則認為SCN的變化滿足了“外部一緻性”。

而“因果一緻性”則是“外部一緻性”的一種特殊情況:事務的發生不僅有前後順序,還要有前後關聯的因果關系。是以“外部一緻性”的覆寫範圍更廣,“因果一緻性”隻是其中的一種情況,如果滿足了“外部一緻性”則一定能滿足“因果一緻性”。OceanBase在實作中滿足了“外部一緻性”,同時也就滿足了“因果一緻性”,本文後半段的内容也主要針對“外部一緻性”來展開。

業内常用的解決方案

那麼,分布式資料庫應如何在全局(跨機器)範圍内保證外部一緻性,進而實作全局一緻的快照隔離級别和多版本并發控制呢?大體來說,業界有兩種實作方式:

1)利用特殊的硬體裝置,如GPS和原子鐘(Atomic Clock),使多台機器間的系統時鐘保持高度一緻,誤差小到應用完全無法感覺的程度。在這種情況下,就可以繼續利用本地系統時間戳作為版本号,同時也能滿足全局範圍内的外部一緻性。

2)版本号不再依賴各個機器自己的本地系統時鐘,所有的資料庫事務通過集中式的服務擷取全局一緻的版本号,由這個服務來保證版本号的單調向前。這樣一來,分布式架構下擷取版本号的邏輯模型和單點架構下的邏輯模型就一樣了,徹底消除了機器之間時鐘差異的因素。

第一種方式的典型代表是Google的Spanner資料庫。它使用GPS系統在全球的多個機房之間保持時間同步,并使用原子鐘確定本地系統時鐘的誤差一直維持在很小的範圍内,這樣就能保證全球多個機房的系統時鐘能夠在一個很高的精度内保持一緻,這種技術在Spanner資料庫内被稱為TrueTime。在此基礎上,Spanner資料庫就可以沿用傳統的方式,以本地系統時間戳作為版本号,而不用擔心破壞全局範圍内的外部一緻性。

這種方式的好處,是軟體的實作比較簡單,并且避免了采用集中式的服務可能會導緻的性能瓶頸。但這種方式也有它的缺點,首先對機房的硬體要求明顯提高,其次“GPS+原子鐘”的方式也不能100%保證多個機器之間的系統時鐘完全一緻,如果GPS或者原子鐘的硬體偏差導緻時間誤差過大,還是會出現外部一緻性被破壞的問題。根據GoogleSpanner論文中的描述,發生時鐘偏差(clock drift)的機率極小,但并不為0。下圖是Google Spanner論文中對上千台機器所做的關于時鐘誤差範圍的統計:

2.0解析系列 | 如何在分布式架構下完美實作“全局資料一緻性”?前言傳統資料庫的實作原理分布式資料庫面臨的挑戰業内常用的解決方案OceanBase的“全局一緻性快照”技術總結

OceanBase則選用了第二種實作方式,即用集中式的服務來提供全局統一的版本号。做這個選擇主要是基于以下考慮:

  • 可以從邏輯上消除機器間的時鐘差異因素,進而徹底避免這個問題。
  • 避免了對特殊硬體的強依賴。這對于一個通用資料庫産品來說尤其重要,我們不能假設所有使用OceanBase資料庫的使用者都在機房裡部署了“GPS+原子鐘”的裝置。

OceanBase的“全局一緻性快照”技術

如前文所述, OceanBase資料庫是利用一個集中式服務來提供全局一緻的版本号。事務在修改資料或者查詢資料的時候,無論請求源自哪台實體機器,都會從這個集中式的服務處擷取版本号,OceanBase則保證所有的版本号單調向前并且和真實世界的時間順序保持一緻。

有了這樣全局一緻的版本号,OceanBase就能根據版本号對全局(跨機器)範圍内的資料做一緻性快照,是以我們把這個技術命名為“全局一緻性快照”。有了全局一緻性快照技術,就能實作全局範圍内一緻的快照隔離級别和多版本并發控制,而不用擔心發生外部一緻性被破壞的情況。

但是,相信有些朋友看到這裡就會産生疑問了,比如:

  • 這個集中式服務裡是如何生成統一版本号的?怎麼能保證單調向前?
  • 這個集中式服務的服務範圍有多大?整個OceanBase叢集裡的事務都使用同一個服務嗎?
  • 這個集中式服務的性能如何?尤其在高并發通路的情況下,是否會成為性能瓶頸?
  • 如果這個集中式服務發生中斷怎麼辦?
  • 如果在事務擷取全局版本号的過程中,發生了網絡異常(比如瞬時網絡抖動),是否會破壞“外部一緻性”?

下面針對這些疑問逐一為大家解答。

首先,這個集中式服務所産生的版本号就是本地的系統時間戳,隻不過它的服務對象不再隻是本地事務,而是全局範圍内的所有事務,是以在OceanBase中這個服務被稱作“全局時間戳服務(Global Timestamp Service,簡稱GTS)”。由于GTS服務是集中式的,隻從一個系統時鐘裡擷取時間戳,是以能保證擷取的時間戳(即版本号)一定是單調向前的,并且一定和真實世界的時間順序保持一緻。

那麼,是否一個OceanBase資料庫叢集中隻有一個GTS服務,叢集中所有的事務都從這裡擷取時間戳呢?對OceanBase資料庫有了解的朋友都知道,“租戶”是OceanBase中實作資源隔離的一個基本單元,比較類似傳統資料庫中“執行個體”的概念,不同租戶之間的資料完全隔離,沒有一緻性要求,也無需實作跨租戶的全局一緻性版本号,是以OceanBase資料庫叢集中的每一個“租戶”都有一個單獨的GTS服務。這樣做不但使GTS服務的管理更加靈活(以租戶為機關),而且也将叢集内的版本号請求分流到了多個GTS服務中,大大減少了因單點服務導緻性能瓶頸的可能。下面是OceanBase資料庫叢集中GTS服務的簡單示意圖:

2.0解析系列 | 如何在分布式架構下完美實作“全局資料一緻性”?前言傳統資料庫的實作原理分布式資料庫面臨的挑戰業内常用的解決方案OceanBase的“全局一緻性快照”技術總結

說到性能,經過實測,單個GTS服務能夠在1秒鐘内響應2百萬次申請時間戳(即版本号)的請求,是以隻要租戶内的QPS不超過2百萬,就不會遇到GTS的性能瓶頸,實際業務則很難觸及這個上限。雖然GTS的性能不是一個問題,但從GTS擷取時間戳畢竟比擷取本地時間戳有更多的開銷,至少網絡的時延是無法避免的,對此我們也做過實測,在滿負荷壓測并且網絡正常的情況下,和采用本地時間戳做版本号相比,采用GTS對性能所帶來的影響不超過5%,絕大多數應用對此都不會有感覺。

除了保證GTS的正常處理性能之外,OceanBase資料庫還在不影響外部一緻性的前提下,對事務擷取GTS的流程做了優化,比如:

  • 将某些GTS請求轉化為本地請求,從本地控制檔案中擷取版本号,避免了網絡傳輸的開銷;
  • 将多個GTS請求合并為一個批量請求,以提高GTS的全局吞吐量;
  • 利用“本地GTS緩存技術”,減少GTS請求的次數。

這些優化措施進一步提高了GTS的處理效率,是以,使用者完全不用擔心GTS的性能。

前面所說的都是正常情況,但對于集中式服務來說一定要考慮異常情況。首先就是高可用的問題,GTS服務也像OceanBase中基本的資料服務一樣,以Paxos協定實作了高可用,如果GTS服務由于異常情況(比如當機)而中斷,那麼OceanBase會根據Paxos協定自動選出一個新的服務節點,整個過程自動而且迅速(1~15秒),無需人工幹預。

2.0解析系列 | 如何在分布式架構下完美實作“全局資料一緻性”?前言傳統資料庫的實作原理分布式資料庫面臨的挑戰業内常用的解決方案OceanBase的“全局一緻性快照”技術總結

那如果發生網絡異常怎麼辦?比如網絡抖動了10秒鐘,會不會影響版本号的全局一緻性,并進而影響外部一緻性?對資料庫來說,外部一緻性反映的是真實世界中“完整事務”之間的前後順序,比如前面說到的T1 -> T2-> T3,其實準确來說是T1's Begin-> T1's End -> T2's Begin -> T2's End -> T3's Begin -> T3's End,即任意兩個完整的事務視窗之間沒有任何重疊。如果發生了重疊,則事務之間并不具備真正的“先後順序”,外部一緻性也就無從談起。

是以,不管網絡如何異常,隻要真實世界中的“完整事務”滿足這種前後順序,全局版本号就一定會滿足外部一緻性。

最後,如果事務發現GTS響應過慢,會重新發送GTS請求,以避免由于特殊情況(如網絡丢包)而導緻事務的處理被GTS請求卡住。

總之,GTS在設計和開發的過程中已經考慮到了諸多異常情況的處理,確定可以提供穩定可靠的服務。

總結

有了“全局一緻性快照”技術之後,OceanBase資料庫便具備了在全局(跨機器)範圍内實作“快照隔離級别”和“多版本并發控制”的能力,可以在全局範圍内保證“外部一緻性”,并在此基礎之上實作衆多涉及全局資料一緻性的功能,比如全局一緻性讀、全局索引等。

這樣一來,和傳統單點資料庫相比,OceanBase在保留分布式架構優勢的同時,在全局資料一緻性上也沒有任何降級,應用開發者就可以像使用單點資料庫一樣使用OceanBase,完全不必擔心機器之間的底層資料一緻性問題。可以說,借助“全局一緻性快照”技術,OceanBase資料庫完美地實作了分布式架構下的全局資料一緻性!

參考文獻:
  1. Snapshot isolation
  2. Multiversionconcurrency control
  3. Isolation(database systems)
  4. Spanner (database)
  5. CloudSpanner: TrueTime and External Consistency
  6. Causal consistency