2.0解析系列 | OceanBase 2.0 之 索引實時生效

OB君:本文是 「 OceanBase 2.0 技術解析系列」 的第七篇文章。今天咱們來聊聊數據的持續可用,說說2.0的索引實時生效功能。更多精彩歡迎關注OceanBase公衆號持續訂閱本系列內容!

引言

隨着業務的快速發展,其對數據庫的數據訪問規則是不斷變化的,在數據庫中新建索引來加速業務查詢是很常見的需求。算法

互聯網的業務規模和發展速度對數據庫的索引構建提出了更高的要求,一方面,在海量的業務規模下,非故障致使的停機是不可接受的,這意味着索引構建的同時,正常業務的讀寫請求不能被影響;另外一方面,業務的快速發展和迭代,對索引構建的效率也有着更高的要求,索引更快速的生效,能加速新業務的開發和迭代過程。數據庫

傳統單機關係數據庫通過幾十年的發展,逐漸實現了索引實時生效功能,這些數據庫主要解決的問題是在索引構建的時候,避免長時間的鎖表影響正常的業務請求。而分佈式數據庫因爲其分佈式的特性,在實現索引實時生效時,面臨和單機數據庫不一樣的問題.網絡

OceanBase 1.x中經過把索引構建放在合併流程中避免了這些問題,但並無作到索引實時生效,從用戶執行建立索引表語句到索引表生效須要通過一或兩次合併,OceanBase 2.0中解決了其中的問題,索引構建再也不與合併耦合,用戶執行建立索引後,能當即進入索引構建流程,較大地縮短了索引構建生效時間。架構

本文首先介紹了關係型數據庫索引構建的發展示狀,接着描述了OceanBase 1.x中索引構建遇到的問題,最後分析了OceanBase 2.0的索引構建設計,並給出瞭解決這些問題的方法。併發

索引構建的現狀

根據架構不一樣,關係型數據庫分爲單機數據庫和分佈式數據庫。不一樣的架構下,索引構建的方案有所不一樣,本節將分別介紹業界單機數據庫和分佈式數據庫的索引構建方案。負載均衡

單機數據庫

咱們以MySQL爲例,來描述單機數據庫的索引構建方案。MySQL從5.6開始支持索引實時生效,首先,執行完建立索引語句以後,新事務中新索引表的數據會寫入到Row Log中,與此同時,會等待未往索引表中寫入過數據的事務都結束。當全部的事務都結束後,開始索引構建流程,主要是處理兩部分數據,一部分是等事務結束以後的主錶快照點數據,此部分數據在索引表中是不存在的,須要經過主表數據構建出來,另外一部分數據是記錄在Row Log中的數據,須要應用到最終的索引表中。構建完成以後,將索引設置成可讀寫狀態,進而優化器能使用該索引來優化用戶的查詢。框架

MySQL索引構建的特色以下:運維

  1. 是爲In Place Update的存儲引擎設計的,爲了不索引構建和用戶事務對索引表更新的併發問題,索引構建過程當中的更新數據會記錄到Row Log的特殊存儲中,這部分數據須要從新寫入到索引表中,寫入的時候會有必定的加鎖時間;
  2. 基於快照點的構建流程是串行的,面對大數據量場景下性能可能存在不足;
  3. 元數據只有單版本,在更新相關元數據時,會加鎖。

分佈式數據庫

和單機數據庫不一樣的是,分佈式數據庫的數據分佈和請求執行多是分佈在多臺機器上的,致使索引構建方案也有所不一樣。本節將介紹Google研發的分佈式數據庫F1的索引構建方案。分佈式

如上圖,Google F1採用的是存儲計算分離的架構,架構上總共分爲三層,最底層是分佈式Key-Value存儲引擎,第二層是無狀態的計算層F1 Server,第三層爲代理層,應用程序經過代理層和F1交互。性能

在此種架構下,事務執行時,有可能出現不一樣的SQL語句在不一樣的F1 Server執行的狀況,那麼不一樣的語句可能使用了不一樣版本的關係型元數據(爲了設計和實現簡單,F1只容許系統中同時出現兩種不一樣版本的元數據),這會致使以下問題。

假設元數據版本S1 < S2,且S2比S1多了一張索引表,有以下執行過程。

  1. 在S2版本的F1 Server上執行INSERT語句,因爲S2版本包括索引表,所以會生成索引表相關的KV記錄;
  2. 在S1版本的F1 Server上執行DELETE語句,且和1中INSERT語句使用相同的主鍵,因爲S1版本不包括索引表,所以索引表相關的KV記錄不會被刪除。

當上述事務執行完成後,索引表將會有多餘的中間數據,致使數據表和索引表的數據不一致。

F1爲了解決這個問題,引入了中間狀態和最終狀態,其中中間狀態包括,delete-only和write-only,delete-only表示索引表只能被delete和update,而write-only表示索引表只能被insert、delete和update。最終狀態包括absent和public,分別表示索引表不存在和索引表生效。在上述出問題的場景中,添加索引表的變動通過了absent->write-only->public的過程,因爲absent狀態時,索引表不存在,致使沒法刪除索引表的數據,所以,F1將添加索引的流程變成了absent->delete-only->write-only->public,這樣就能保證索引構建完成後,數據和主表保持一致。

整體來看,Google F1的索引構建方案有以下特色:

F1 Server中最多存在兩個不一樣版本的元數據,這意味着若是有機器在一個元數據更新租約時間內沒有刷新到新版本元數據,那麼F1 Server必需要自動退出以保證這個約束,使得這種方案比較適用於計算存儲分離的架構,另外,爲了不F1 Server頻繁因網絡抖動主動退出,元數據更新租約時間通常是分鐘級別,所以,對於數據量較小的表格構建索引,也須要分鐘級別才能生效。

OceanBase 索引構建方案

本節先簡單介紹下OceanBase的總體架構、存儲引擎特色以及索引表的寫入流程,接着討論OceanBase 1.x索引構建中碰到的問題,最後描述了OceanBase 2.0中的索引構建方案。

總體架構

OceanBase的一個集羣一般由多個zone組成,一個zone由一個或多個ObServer組成的,每一個ObServer都具備計算和存儲的功能。在ObServer中有一個較爲特殊,負責總控服務的節點稱爲RootService,負責管理集羣的元數據和路由信息,其中,元數據是按照多版本方式管理的。OceanBase按照分區的方式管理數據,一張表包含一個或多個分區,每一個分區的數據會存儲在多個zone中,每一個zone都是一份完整的數據拷貝(副本)。每一個分區的副本中會有一個Leader副本,負責處理該分區的讀寫請求。

存儲引擎

OceanBase的存儲引擎是按照Log Structured Merge Tree(LSM Tree)方式組織的,分爲基線數據和增量數據兩部分。基線數據存儲在基線SSTable中,增量數據存儲在Memtable和轉儲SSTable中。基線SSTable按照版本遞增的方式來管理,某個版本的基線SSTable一旦生成後就變成只讀狀態,修改的數據會存儲在Memtable中,當達到必定內存閾值後會先進行minor compaction(轉儲)轉換成轉儲SSTable,當轉儲SSTable達到必定數量時,會將基線SSTable和轉儲SSTable作major compaction(合併),生成更高版本的基線SSTable。

在SSTable和Memtable中數據都是按照表的主鍵排序的,例如,有一張數據表test,包含列c一、c二、c三、c4和c5,其中主鍵爲c1,那麼test的數據是按照列c1的升序方式存儲的;若是在數據表test上建立一個索引index,假設包含列c3和c2,那麼index是按照主鍵c三、c2和c1的升序方式存儲的,其中存儲c1列是爲了方便回表查詢。

索引表的寫入流程

在介紹索引表寫入流程以前,先來看看OceanBase索引表的分區管理方式。OceanBase中索引表分爲局部索引和全局索引。

局部索引是指分區規則和主表相同的索引,因爲分區規則相同,局部索引和主表共用相應的分區,所以,局部索引分區和主表是在同一臺機器上的。

全局索引是指分區規則和主表不一樣的索引,分區規則的不一樣致使了全局索引和主表沒法共用分區,而分區是OceanBase管理的基本單位,分區不一樣意味着全局索引和主表的分區是可能不在同一臺機器上的。

索引表的寫入一般是由主表驅動的,對主表的寫入操做通常分爲INSERT、DELETE和UPDATE,以上面的test主表和index索引表爲例,對於INSERT,假如對test主表執行INSERT INTO test values(a,b,c,d,e),會根據索引表的列生成索引行(c,b,a),寫入到索引表中;對於DELETE,假如對test主表執行DELETE FROM test where c1 = 'a',會經過主表中的數據,獲取索引列c3和c2,假設值爲c和b,拼成完整的索引行(c,b,a)並刪除索引表中對應的行;對於UPDATE,假如對test主表執行UPDATE SET c3 = c' where c1 = 'a',首先會先獲取主表中的數據,獲取索引列c3和c2的數據,假設值爲c和b,拼成完整的行(c,b,a)並刪除索引表中對應的行,而後生成新的行(c',b,a)並寫入索引表。對於惟一索引,更新時會檢查寫入的數據是否知足惟一性約束,具體地,須要檢查索引表已有的數據中是否存在將要寫入的行,若是存在,則會報惟一性衝突。

OceanBase 1.x 索引構建面臨的問題

在OceanBase 1.x中,用戶執行建立索引語句後,會等到集羣下次合併時開始構建,構建過程當中先等待主表合併到新版本後,再基於主表的最新版本的基線SSTable數據,構建出索引表的數據。OceanBase 1.x索引構建有以下問題:

  1. OceanBase 1.x缺乏指定快照點讀取數據的功能,索引構建依賴合併的快照點;
  2. OceanBase 1.x中只有局部索引,能經過同臺機器的主表數據構建索引表的數據,而OceanBase 2.0中新增了全局索引,構建過程變成了分佈式排序,如何以較小代價實現分佈式排序呢?
  3. OceanBase 1.x中主表和索引表SSTable版本號是統一管理的,二者的版本號須要統一推動,而若是索引實時構建的話,可能在索引構建的同時,主表數據已經更新了多個版本,OceanBase 1.x的SSTable版本管理方法沒法知足該需求。

OceanBase 2.0 索引構建方案

本節經過描述整個索引構建的流程,來介紹索引構建的方案設計以及如何解決相關問題的。整體上,OceanBase 2.0中,索引構建分爲準備、構建、拷貝和收尾共四個階段。

1)準備階段

在索引構建準備階段主要作了兩件事情:

  1. 生成索引表的元數據信息,其中索引表設置成只寫狀態,根據LSM Tree存儲引擎的特色,索引表構建期間的數據直接寫入到索引表的Memtable中,帶來的好處是這部分數據直接成爲索引表的一部分,後面無須再將這部分數據插入到索引表中,而且能夠複用前面小節描述的索引表寫入流程的代碼;
  2. 等待以前未往索引表插入過數據的事務結束,當全部的事務都結束後,獲取構建快照點,構建階段將基於此快照點掃描主表數據,並寫入到索引表基線SSTable中,而用戶事務產生的數據寫入到Memtable中,這樣就無須處理索引表構建和用戶事務同時對索引表寫入致使的併發更新問題。

步驟1中涉及到元數據的變動,OceanBase中採用多版本方式管理元數據,爲了不分佈式環境下不一樣ObServer元數據版本不一樣帶來的問題,採用以下方案解決。

  1. ObProxy將同一個事務的請求發送給同一個中控ObServer處理,這樣能夠保證事務的不一樣語句在中控ObServer看到的元數據版本是遞增的,從而避免語句級別的元數據版本回退致使數據不一致的問題;
  2. 同一條語句可能會涉及到多個ObServer,可能會出現多個ObServer的元數據版本不一樣的狀況,若是出現和中控ObServer的元數據版本不一致時,則進行語句重試。

準備階段最重要的輸出爲快照點,在傳統單機數據庫中,獲取快照點比較容易的,通常獲取系統當前時間戳便可,但在分佈式數據庫中,因爲每臺機器的時間戳不多是徹底一致的,所以,不能簡單的獲取某個機器的時間戳做爲快照點。OceanBase 2.0中實現了租戶級全局時間戳,每一個租戶提供一個授時服務,每一個租戶的事務版本號都經過授時服務來得到,一樣,索引構建準備階段的快照點也經過授時服務來得到。

2)構建階段

構建階段的目標是基於主錶快照點掃描出索引表所需數據,並按照索引列排序規則生成索引表基線SSTable數據。

在OceanBase 1.x中,Memtable轉換成轉儲SSTable時,多版本的數據會被歸併成單版本的,所以,數據一旦發生轉儲,且快照點落在轉儲SSTable範圍內,則沒法讀取快照點數據。爲了可以基於某個快照點掃描主表的數據,OceanBase 2.0中,Memtable轉成轉儲SSTable時,會把Memtable中全部數據及其版本號都記錄下來,從而掃描數據時,能根據快照點和數據的版本號,讀取到所需版本的數據。

排序根據索引類型不一樣,執行過程也不相同,對於局部索引,索引表分區和主表相同,所以,構建階段的數據流動僅僅是在本機,而對於全局索引,索引表分區和主表不一樣,一個索引表分區的數據一般來自主表的多個分區,而這些分區多是在不一樣ObServer上的,是一個分佈式排序過程,整個構建過程描述成了一個SQL的plan。

和麪向OLTP的分佈式執行不一樣,索引構建的分佈式排序更關注容災,在機器故障等狀況下,能以較小的代價快速恢復,OceanBase 2.0的SQL執行框架中支持構建過程的中間結果持久化,構建過程出現機器宕機時,只須要選擇其餘機器從新執行故障機器相關的任務。同時,爲了加速構建過程,將數據掃描和排序都作了並行化,充分利用磁盤和CPU的並行能力。

構建階段可能會遇到合併,此時主表的基線SSTable的版本會增長,在索引表構建好以後,其版本號是落後於主表的。爲了能描述這種場景,咱們對基線SSTable管理功能作了重構,每一個SSTable單獨管理版本號,可以保證索引構建時,即便索引表落後主表多個版本,也能構建成功。

在生產環境,主表數據量一般比較大,索引構建每每是比較耗時的操做,爲了不索引構建對正經常使用戶請求產生影響,OceanBase 2.0中會基於用戶IO請求來限速,當用戶請求的IO響應時間超過必定閾值時,會自動限制構建過程當中IOPS。

3)拷貝階段

鑑於索引構建比較耗時,OceanBase 2.0中只在單副本上構建索引表,其餘副本從構建好的副本拷貝數據。在單副本構建期間寫入索引表的數據會經過一致性算法同步到索引表多個副本上,所以,拷貝階段僅僅須要將構建好的基線SSTable拷貝到其餘的副本上。和構建同樣,咱們對拷貝過程也作了並行化,每一個分區會按照數據量切分紅多個任務,由多個線程批量地執行任務。爲了不併行拷貝過分的消耗資源,會對拷貝階段總體網絡帶寬做限制,同時也提供了拷貝的並行度控制,方便運維。

4)收尾階段

收尾階段分爲兩步,分別是數據校驗和索引生效。數據校驗根據索引類型的不一樣,會作不一樣的操做,對於普通索引,會根據構建階段計算的主表的列校驗和索引表的列校驗和進行比對,保證構建出來的索引表的數據和主表的數據是一致的;對於惟一索引,由於構建過程當中也有可能寫入數據,而構建過程當中索引表的基線數據還未構建完成,惟一性校驗可能不完整,所以,須要在索引表基線數據構建和拷貝完成後,對惟一索引作惟一性校驗,同時爲了保證索引構建的數據正確性,咱們採用將主表和索引表數據進行校驗,既驗證了數據正確性,也驗證了索引是否知足惟一性。

當數據校驗經過時,將索引表設置成可讀寫狀態,進而SQL優化器能使用新的索引來加速查詢,當數據校驗不經過時(通常是惟一索引惟一性約束不知足),會將索引設置成不可用狀態。

總結

OceanBase的索引實時生效包括兩層含義,第一是索引構建過程從合併中解耦,用戶觸發後能當即進行構建流程,第二是經過專門爲LSM Tree存儲引擎優化的構建流程,構建流程的並行化,單副本構建,副本拷貝的並行化,以及容災時快速恢復等技術手段,有效地加快了索引生效的速度。

OceanBase的索引構建時能對佔用的資源作控制,減小對正經常使用戶請求的影響。經過完備的數據校驗,保證了構建完成的索引表的數據正確性。另外,當前的索引構建方案對計算存儲一體化以及計算存儲分離的架構都是適用的。

參考文獻

1. Innodb Online DDL Operations

2. Online, Asynchronous Schema Change in F1

OceanBase技術交流羣

— 想了解更多OceanBase 2.0新特性?

— 想與螞蟻金服OceanBase的一線技術專家深刻交流?

掃描下方二維碼聯繫小編,快速加入OceanBase技術交流羣!

2.0解析系列文章

相關文章
相關標籤/搜索