OceanBase 使用通用服務器硬件,依賴本地存儲,分布式部署使用的多個服務器也是對等的,沒有特殊的硬件要求。OceanBase 的分布式數據庫處理采用 Shared Nothing 架構,數據庫內的 SQL 執行引擎具有分布式執行能力。
OceanBase 在服務器上會運行叫做 observer 的單進程程序作為數據庫的運行實例,使用本地的文件存儲數據和事務 Redo 日志。
OceanBase 集群部署需要配置可用區(Zone),由若干個服務器組成。可用區是一個邏輯概念,表示集群內具有相似硬件可用性的一組節點,它在不同的部署模式下代表不同的含義。例如,當整個集群部署在同一個數據中心(IDC)內的時候,一個可用區的節點可以屬于同一個機架,同一個交換機等。當集群分布在多個數據中心的時候,每個可用區可以對應于一個數據中心。
用戶存儲的數據在分布式集群內部可以存儲多個副本,用于故障容災,也可以用于分散讀取壓力。在一個可用區內部數據只有一個副本,不同的可用區可以存儲同一個數據的多個副本,副本之間由共識協議保證數據的一致性。
OceanBase 內置多租戶特性,每個租戶對于使用者是一個獨立的數據庫,一個租戶能夠在租戶級別設置租戶的分布式部署方式。租戶之間 CPU、內存和 IO 都是隔離的。
OceanBase 的數據庫實例內部由不同的組件相互協作,這些組件從底層向上由存儲層、復制層、均衡層、事務層、SQL 層、接入層組成。
存儲層
存儲層以一張表或者一個分區為粒度提供數據存儲與訪問,每個分區對應一個用于存儲數據的Tablet(分片),用戶定義的非分區表也會對應一個 Tablet。
Tablet 的內部是分層存儲的結構,總共有 4 層。DML 操作插入、更新、刪除等首先寫入 MemTable,等到 MemTable 達到一定大小時轉儲到磁盤成為 L0 SSTable。L0 SSTable 個數達到閾值后會將多個 L0 SSTable 合并成一個 L1 SSTable。在每天配置的業務低峰期,系統會將所有的 MemTable、L0 SSTable 和 L1 SSTable 合并成一個 Major SSTable。
每個 SSTable 內部是以 2MB 定長宏塊為基本單位,每個宏塊內部由多個不定長微塊組成。
Major SSTable 的微塊會在合并過程中用編碼方式進行格式轉換,微塊內的數據會按照列維度分別進行列內的編碼,編碼規則包括字典/游程/常量/差值等,每一列壓縮結束后,還會進一步對多列進行列間等值/子串等規則編碼。編碼能對數據大幅壓縮,同時提煉的列內特征信息還能進一步加速后續的查詢速度。
在編碼壓縮之后,還可以根據用戶指定的通用壓縮算法進行無損壓縮,進一步提升數據壓縮率。
復制層
復制層使用日志流(LS、Log Stream)在多副本之間同步狀態。每個 Tablet 都會對應一個確定的日志流,DML 操作寫入 Tablet 的數據所產生的 Redo 日志會持久化在日志流中。日志流的多個副本會分布在不同的可用區中,多個副本之間維持了共識算法,選擇其中一個副本作為主副本,其他的副本皆為從副本。Tablet 的 DML 和強一致性查詢只在其對應的日志流的主副本上進行。
通常情況下,每個租戶在每臺機器上只會有一個日志流的主副本,可能存在多個其他日志流的從副本。租戶的總日志流個數取決于 Primary Zone 和 Locality 的配置。
日志流使用自研的 Paxos 協議實現了將 Redo 日志在本服務器持久化,同時通過網絡發送給日志流的從副本,從副本在完成各自持久化后應答主副本,主副本在確認有多數派副本都持久化成功后確認對應的 Redo 日志持久化成功。從副本利用 Redo 日志的內容實時回放,保證自己的狀態與主副本一致。
日志流的主副本在被選舉成為主后會獲得租約(Lease),正常工作的主副本在租約有效期內會不停的通過選舉協議延長租約期。主副本只會在租約有效時執行主的工作,租約機制保證了數據庫異常處理的能力。
復制層能夠自動應對服務器故障,保障數據庫服務的持續可用。如果出現少于半數的從副本所在服務器故障,因為還有多于半數的副本正常工作,數據庫的服務不受影響。如果主副本所在服務器出現問題,其租約會得不到延續,待其租約失效后,其他從副本會通過選舉協議選舉出新的主副本并授予新的租約,之后即可恢復數據庫的服務。
均衡層
新建表和新增分區時,系統會按照均衡原則選擇合適的日志流創建 Tablet。當租戶的屬性發生變更,新增了機器資源,或者經過長時間使用后,Tablet 在各臺機器上不再均衡時,均衡層通過日志流的分裂和合并操作,并在這個過程中配合日志流副本的移動,讓數據和服務在多個服務器之間再次均衡。
當租戶有擴容操作,獲得更多服務器資源時,均衡層會將租戶內已有的日志流進行分裂,并選擇合適數量的 Tablet 一同分裂到新的日志流中,再將新日志流遷移到新增的服務器上,以充分利用擴容后的資源。當租戶有縮容操作時,均衡層會把需要縮減的服務器上的日志流遷移到其他服務器上,并和其他服務器上已有的日志流進行合并,以縮減機器的資源占用。
當數據庫長期使用后,隨著持續創建刪除表,并且寫入更多的數據,即使沒有服務器資源數量變化,原本均衡的情況可能被破壞。最常見的情況是,當用戶刪除了一批表后,刪除的表可能原本聚集在某一些機器上,刪除后這些機器上的 Tablet 數量就變少了,應該把其他機器的 Tablet 均衡一些到這些少的機器上。均衡層會定期生成均衡計劃,將 Tablet 多的服務器上日志流分裂出臨時日志流并攜帶需要移動的 Tablet,臨時日志流遷移到目的服務器后再和目的服務器上的日志流進行合并,以達成均衡的效果。
事務層
事務層保證了單個日志流和多個日志流DML操作提交的原子性,也保證了并發事務之間的多版本隔離能力。
原子性
一個日志流上事務的修改,即使涉及多個 Tablet,通過日志流的 write-ahead log 可以保證事務提交的原子性。事務的修改涉及多個日志流時,每個日志流會產生并持久化各自的write-ahead log,事務層通過優化的兩階段提交協議來保證事務提交的原子性。
事務層會選擇一個事務修改的一個日志流產生協調者狀態機,協調者會與事務修改的所有日志流通信,判斷 write-ahead log 是否持久化,當所有日志流都完成持久化后,事務進入提交狀態,協調者會再驅動所有日志流寫下這個事務的 Commit 日志,表示事務最終的提交狀態。當從副本回放或者數據庫重啟時,已經完成提交的事務都會通過 Commit 日志確定各自日志流事務的狀態。
宕機重啟場景下,宕機前還未完成的事務,會出現寫完 write-ahead log 但是還沒有Commit 日志的情況,每個日志流的 write-ahead log 都會包含事務的所有日志流列表,通過此信息可以重新確定哪個日志流是協調者并恢復協調者的狀態,再次推進兩階段狀態機,直到事務最終的 Commit 或 Abort 狀態。
隔離性
GTS 服務是一個租戶內產生連續增長的時間戳的服務,其通過多副本保證可用性,底層機制與上面復制層所描述的日志流副本同步機制是一樣的。
每個事務在提交時會從 GTS 獲取一個時間戳作為事務的提交版本號并持久化在日志流的write-ahead log 中,事務內所有修改的數據都以此提交版本號標記。
每個語句開始時(對于 Read Committed 隔離級別)或者每個事務開始時(對于Repeatable Read 和 Serializable 隔離級別)會從 GTS 獲取一個時間戳作為語句或事務的讀取版本號。在讀取數據時,會跳過事務版本號比讀取版本號大的數據,通過這種方式為讀取操作提供了統一的全局數據快照。
SQL 層
SQL 層將用戶的 SQL 請求轉化成對一個或多個 Tablet 的數據訪問。
SQL 層組件
SQL 層處理一個請求的執行流程是:Parser、Resolver、Transformer、Optimizer、Code Generator、Executor。
Parser 負責詞法/語法解析,Parser 會將用戶的 SQL 分成一個個的 "Token",并根據預先設定好的語法規則解析整個請求,轉換成語法樹(Syntax Tree)。
Resolver 負責語義解析,將根據數據庫元信息將 SQL 請求中的 Token 翻譯成對應的對象(例如庫、表、列、索引等),生成的數據結構叫做 Statement Tree。
Transformer 負責邏輯改寫,根據內部的規則或代價模型,將 SQL 改寫為與之等價的其他形式,并將其提供給后續的優化器做進一步的優化。Transformer 的工作方式是在原Statement Tree 上做等價變換,變換的結果仍然是一棵 Statement Tree。
Optimizer(優化器)為 SQL 請求生成最佳的執行計劃,需要綜合考慮 SQL 請求的語義、對象數據特征、對象物理分布等多方面因素,解決訪問路徑選擇、聯接順序選擇、聯接算法選擇、分布式計劃生成等問題,最終生成執行計劃。
Code Generator(代碼生成器)將執行計劃轉換為可執行的代碼,但是不做任何優化選擇。
Executor(執行器)啟動 SQL 的執行過程。
在標準的 SQL 流程之外,SQL 層還有 Plan Cache 能力,將歷史的執行計劃緩存在內存中,后續的執行可以反復執行這個計劃,避免了重復查詢優化的過程。配合 Fast-parser 模塊,僅使用詞法分析對文本串直接參數化,獲取參數化后的文本及常量參數,讓 SQL 直接命中 Plan Cache,加速頻繁執行的 SQL。
多種計劃
SQL 層的執行計劃分為本地、遠程和分布式三種。本地執行計劃只訪問本服務器的數據。遠程執行計劃只訪問非本地的一臺服務器的數據。分布式計劃會訪問超過一臺服務器的數據,執行計劃會分成多個子計劃在多個服務器上執行。
SQL 層并行化執行能力可以將執行計劃分解成多個部分,由多個執行線程執行,通過一定的調度的方式,實現執行計劃的并行處理。并行化執行可以充分發揮服務器 CPU 和 IO 處理能力,縮短單個查詢的響應時間。并行查詢技術可以用于分布式執行計劃,也可以用于本地執行計劃。
接入層
OceanBase 數據庫代理(OceanBase Database Proxy,ODP)是 OceanBase 數據庫的接入層,負責將用戶的請求轉發到合適的 OceanBase 實例上進行處理。
ODP 是獨立的進程實例,獨立于 OceanBase 的數據庫實例部署。ODP 監聽網絡端口,兼容 MySQL 網絡協議,支持使用 MySQL 驅動的應用直接連接 OceanBase。
ODP 能夠自動發現 OceanBase 集群的數據分布信息,對于代理的每一條 SQL 語句,會盡可能識別出語句將訪問的數據,并將語句直接轉發到數據所在服務器的 OceanBase 實例。
ODP 有兩種部署方式,一種是部署在每一個需要訪問數據庫的應用服務器上,另一種是部署在與 OceanBase 相同的機器上。第一種部署方式下,應用程序直接連接部署在同一臺服務器上的 ODP ,所有的請求會由 ODP 發送到合適的 OceanBase 服務器。第二種部署方式下,需要使用網絡負載均衡服務將多個 ODP 聚合成同一個對應用提供服務的入口地址。