第三屆oceanbase數(shù)據(jù)庫大賽決賽|賽題相關(guān)源碼解析
官方答疑ppt以及官方出品的源碼解析書籍和官方文檔已經(jīng)講的非常好了,這篇文章主要把和此次比賽相關(guān)的知識點匯總一下
oceanbase4.0架構(gòu)簡析
oceanbase基本架構(gòu)
總體上,OceanBase采用了使用普通服務(wù)器和數(shù)據(jù)中心網(wǎng)絡(luò)組成的無共享(Shared-Nothing)的集群架構(gòu),集群由若干完全對等的計算機(稱為節(jié)點)組成,每個節(jié)點都有其私有的物理資源(包括CPU、內(nèi)存、硬盤等),并且在這些物理資源上運行著獨立的存儲引擎、SQL引擎、事務(wù)引擎等。集群中各個節(jié)點之間相互獨立,但通過連接彼此的網(wǎng)絡(luò)設(shè)備相互協(xié)調(diào),共同作為一個整體完成用戶的各種請求。由于節(jié)點之間的獨立性,使得OceanBase具備可擴展、高可用、高性能、低成本等核心特性。
所有服務(wù)節(jié)點都支持 SQL 計算和數(shù)據(jù)存儲,每個節(jié)點自主管理所服務(wù)的分區(qū)數(shù)據(jù)。整個集群只有一種數(shù)據(jù)庫服務(wù)進程,無外部服務(wù)依賴,運維管理簡單。對外提供統(tǒng)一的數(shù)據(jù)庫服務(wù),支持 ACID 事務(wù)和全局索引,對應(yīng)用開發(fā)來說與單機無異。OceanBase 可以靈活的基于用戶基礎(chǔ)設(shè)施,支持同城三中心、兩地三中心、三地五中心等多種架構(gòu)。
比賽的代碼是基于4.x版本,而源碼解析書籍是基于3.x版本,4.x版本最大的一個特性是單機分布式一體化,兼顧了分布式架構(gòu)的可擴展性以及單機架構(gòu)的性能,當(dāng)sql語句或者事務(wù)只涉及單機內(nèi)的分區(qū)的時候,便沒有多機訪問的開銷,甚至性能比一些單機數(shù)據(jù)庫更好,畢竟ob做了極致的優(yōu)化。
此處僅作簡單介紹,sql引擎存儲引擎等的架構(gòu)介紹可以查詢互聯(lián)網(wǎng)上其他資料。
oceanbase概念簡析
為了組織好集群中的數(shù)據(jù)庫,OceanBase集群采用了多個不同粒度的邏輯概念。下面對這些ob的概念術(shù)語進行講解,好對ob有一個基本的認識:
分區(qū)(Partition)
OceanBase中數(shù)據(jù)分布的基本單元是分區(qū),這里的分區(qū)和數(shù)據(jù)庫領(lǐng)域常用的分區(qū)概念相同,即表中一部分元組所構(gòu)成的集合。分區(qū)也是OceanBase中數(shù)據(jù)的物理存儲單位,每一個分區(qū)都有自己的標識和存儲設(shè)置。OceanBase支持多種分區(qū)策略:①范圍(Range)分區(qū);②Hash分區(qū)和Key分區(qū);③列表(List)分區(qū);④組合分區(qū)。
副本(Replica)
為了提高可靠性以及并行性,OceanBase數(shù)據(jù)庫中會以分區(qū)為單位建立副本并分散在整個集群中,同一個分區(qū)的多個副本一起組成一個Paxos復(fù)制組。其中每時每刻有一個副本作為主副本(Leader),所有對該分區(qū)的寫請求都會在主副本上進行,其他副本通過Paxos協(xié)議復(fù)制主副本上的日志來保持同步,這些副本被稱為Follower。
Observer
OBServer可以視為“邏輯”服務(wù)器,一臺物理服務(wù)器上可以部署一個或者多個OBServer。每臺OBServer都包含SQL引擎、事務(wù)引擎和存儲引擎,并管理多個數(shù)據(jù)分區(qū),用戶的SQL查詢經(jīng)過SQL引擎解析優(yōu)化后轉(zhuǎn)化為事務(wù)引擎和存儲引擎的內(nèi)部調(diào)用,最終作用在OBServer上的數(shù)據(jù)分區(qū)上。在OceanBase內(nèi)部,OBServer由其所在物理服務(wù)器的IP地址和OBServer的服務(wù)端口唯一標識。
集群中的每一臺OBServer都能接收客戶端(例如obclient)的直接連接。OBServer都能感知到集群中其他OBServer的存在以及它們各自的身份(例如分區(qū)的Leader等),且OBServer都有完整的SQL、存儲、事務(wù)引擎,因此每一臺OBServer都可以通過協(xié)調(diào)其他OBServer共同完成收到的客戶端SQL請求。
在OceanBase集群的前端會配置一個由OBProxy組成的轉(zhuǎn)發(fā)層和一個負載均衡層,通過這兩者配合將客戶端的請求路由到最合適的OBServer,從而實現(xiàn)全集群的負載均衡。
1)如果收到的是查詢等DML請求,那么收到請求的OBServer會完成SQL的解析、執(zhí)行計劃確定等動作,在執(zhí)行數(shù)據(jù)操作時通過OBServer間的RPC調(diào)用讓目標數(shù)據(jù)(分區(qū))所在的OBServer協(xié)助完成數(shù)據(jù)的操作,然后將結(jié)果匯聚到接收請求的OBServer上,最后由它返回給客戶端。
2)如果收到的是DDL請求,則收到請求的OBServer會將請求最終交給RootService所在的OBServer,由它調(diào)用模式服務(wù)組件提供的功能完成DDL操作。
RootService
總控服務(wù),負責(zé)整個集群的資源調(diào)度、資源分配、數(shù)據(jù)分布信息管理以及模式服務(wù)等功能。
在集群中會有若干可用區(qū)(通常是三個)提供總控服務(wù)(RootService),整個集群中的多個總控服務(wù)呈現(xiàn)為一主多備的配置:集群中每一時刻僅有一個總控服務(wù)(可稱為活躍總控服務(wù))生效,其他總控服務(wù)在活躍總控服務(wù)失效時會選出一個接替其工作。運行著活躍總控服務(wù)的OBServer也被稱為RootServer。跨zone主備 保證核心表、系統(tǒng)表高可用,保證集群管理服務(wù)、元數(shù)據(jù)服務(wù)高可用。
而 RS 不是獨立的進程,在4.0版本之后,RS是啟動在系統(tǒng)日志流leader所在的observer上面的一組服務(wù)。3.x版本當(dāng)中則是啟動在1 號表 (__all_core_table) leader 所在 ObServer 上的一組服務(wù)。
地域(Region)
Region指一個地域或者城市,一個Region包含一個或者多個可用區(qū),不同Region通常距離較遠。通過Region的概念,OceanBase可以支持集群跨城市部署,城市之間距離通常比較遠,從而支持多城市級別的容災(zāi)。
可用區(qū)/區(qū)(Zone)
Zone是可用區(qū)(Availability Zone)的簡稱。一個OceanBase集群,由若干個可用區(qū)組成,每個可用區(qū)又包括多臺OBServer,如下圖所示。為了數(shù)據(jù)安全性和高可用性,一般會把分區(qū)的多個副本分布在不同的可用區(qū)中,從而使得單個可用區(qū)故障不影響數(shù)據(jù)庫服務(wù)。
日志流
日志流是一組連續(xù)遞增的日志數(shù)據(jù),記錄了數(shù)據(jù)庫中的所有變更操作。變更是所有數(shù)據(jù)同步的基石,從副本、主庫與備庫、備份數(shù)據(jù)都依賴日志中記錄的變更信息。日志流代表了一批數(shù)據(jù)的集合,包括若干 Tablet 和有序的 Redo 日志流。
4.x版本將物理的變更記錄聚合成了組織良好的若干日志流:一個系統(tǒng)日志流和多個用戶日志流。故障恢復(fù)、日志歸檔、備庫同步等均使用一套物理變更信息。
在一個租戶內(nèi),一個日志流允許有多個副本,多個副本之間基于 Paxos 協(xié)議同步數(shù)據(jù)。每個副本,包括存儲在磁盤上的 Tablet 的靜態(tài)數(shù)據(jù)(SSTable)、存儲在內(nèi)存上的 Tablet 的增量數(shù)據(jù)(MemTable)、以及記錄事務(wù)的日志三類主要的數(shù)據(jù)。根據(jù)存儲數(shù)據(jù)種類的不同,副本有幾種不同的類型,以支持不同業(yè)務(wù)在在數(shù)據(jù)安全、性能伸縮性、可用性、成本等之間的選擇。
數(shù)據(jù)分區(qū):就是將數(shù)據(jù)按照hash或者range之類的規(guī)則進行打散,每個分區(qū)都有其對應(yīng)的數(shù)據(jù)存儲對象,稱之為分片,也叫做tablet或者partition,它具備存儲數(shù)據(jù)的能力,支持在機器之間遷移,是數(shù)據(jù)均衡的最小單位。
ob3.x版本的會對數(shù)據(jù)做預(yù)分區(qū),每個分區(qū)一個日志流,日志流個數(shù)越多,消耗的 CPU 和內(nèi)存也會更多。在分布式場景下,事務(wù)的原子性、持久性會靠多條日志流來共同保障,比如分布式事務(wù)的兩階段提交、Paxos 一致性協(xié)議等,系統(tǒng)開銷相對單機的單條日志流會增多。在大規(guī)模場景下,這個開銷并不關(guān)鍵,但是當(dāng)ob走向中小規(guī)模的企業(yè)中,如果日志流特別多,在越小規(guī)模下開銷顯得就會越大。
而在4.x版本,把多條日志流合并為一條日志流,極大降低了系統(tǒng)負載。每臺機器上的每個租戶只有一個日志流,這個租戶上的所有數(shù)據(jù)分區(qū)都動態(tài)綁定在該日志流之上,從而避免了大量日志流導(dǎo)致的 overhead。另外,分區(qū)到日志流是動態(tài)綁定的,當(dāng)系統(tǒng)增加新的服務(wù)器時,可以把分區(qū)從源端的日志流動態(tài)解綁并重新綁定到目的端的日志流,集群會自動把數(shù)據(jù)遷移到另一個日志流里面,做數(shù)據(jù)的負載均衡,整個遷移動作都由底層自動完成,對應(yīng)用透明,從而實現(xiàn)分區(qū)動態(tài)遷移。
大賽涉及模塊源碼解析
bootstrap流程源碼簡析
客戶端發(fā)起ALTER SYSTEM BOOTSTRAP SQL指令
ObBootstrapExecutor::execute發(fā)起OB_BOOTSTRAP RPC
ObService::bootstrap開始執(zhí)行bootstrap任務(wù)
首先執(zhí)行pre_bootstrap,完成一些前置檢查/分配系統(tǒng)租戶資源
Prepare bootstrap的關(guān)鍵步驟就是發(fā)起OB_CREATE_LS RPC請求創(chuàng)建1號日志流,然后等待1號日志流創(chuàng)建完成并且選主完畢之后發(fā)起OB_EXECUTE_BOOTSTRAP RPC到master rootservice處理執(zhí)行execute_bootstrap
1、然后執(zhí)行execute bootstrap,該流程當(dāng)中,主要的函數(shù)及其作用如下表所示:
這里挑出create_all_schema函數(shù)重點介紹一下:
整個流程的時序圖如下圖所示:
Create tenant流程簡析
創(chuàng)建租戶的核心流程以及對應(yīng)函數(shù)主要功能如下表格所示,按照執(zhí)行的先后順序:
create_tenant_user_ls流程涉及到一個狀態(tài)機,可以結(jié)合下圖閱讀相關(guān)流程的代碼:
關(guān)鍵組件
總控服務(wù)
(1)元數(shù)據(jù)管理
RS通過心跳機制監(jiān)控集群中各個OBServer的存活狀態(tài),并同步更新系統(tǒng)表,以及進行異常處理。同時也通過心跳向其他OBServer傳輸配置變更、模式變化等多種信息。all_root_table存放所有系統(tǒng)表的分區(qū)信息,all_tenant_meta_table存放所有用戶表的分區(qū)信息,這些信息也由RS統(tǒng)一管理,其他OBServer執(zhí)行請求時可以通過RS服務(wù)獲取這些信息來定位要操縱的數(shù)據(jù)。RS通過RPC主動獲取及定時任務(wù)維護元數(shù)據(jù)的準確性,通過位置緩存(Location Cache)模塊對內(nèi)部其他模塊及外部OBProxy提供位置信息及副本級元信息的查詢服務(wù)。為了避免隊列線程池模型造成多個OBServer出現(xiàn)循環(huán)依賴問題,為每一級系統(tǒng)表使用單獨隊列,例如all_core_table、all_root_table、普通系統(tǒng)表、用戶表分別有各自獨立隊列及工作線程。
(2)集群資源管理
集群資源管理包括Leader管理、分區(qū)負載均衡、資源單元(Resource Unit)負載均衡等任務(wù)。Leader管理包括將分區(qū)組中所有分區(qū)的Leader切到一起、將Leader切到主可用區(qū)(Primary Zone)、輪轉(zhuǎn)合并及隔離切主等場景。分區(qū)負載均衡是指在租戶的多個資源單元內(nèi)調(diào)整分區(qū)組的分布,使得單元的負載比較均衡。資源單元負載均衡是指在多個OBServer間調(diào)度資源單元,使得OBServer的負載比較均衡。
(3)版本合并管理
不同于小版本凍結(jié)(轉(zhuǎn)儲)由各個OBServer自行處理,大版本凍結(jié)(合并)由RS協(xié)調(diào)發(fā)起,是一個由RS和所有分區(qū)Leader組成的兩階段分布式事務(wù)。某個分區(qū)無主會導(dǎo)致大版本凍結(jié)失敗。合并可以由業(yè)務(wù)寫入(轉(zhuǎn)儲達到一定的次數(shù),由全局參數(shù)控制)觸發(fā),也可以定時觸發(fā)(例如每日合并,一般設(shè)置于業(yè)務(wù)低峰期)或手動觸發(fā)。RS還可以控制輪轉(zhuǎn)合并,從而減少合并對業(yè)務(wù)的影響。
(4)執(zhí)行管理命令
RS是管理命令執(zhí)行的入口,包括BOOTSTRAP命令、ALTER SYSTEM命令和其他DDL命令。BOOTSTRAP是系統(tǒng)的自舉過程,主要用于創(chuàng)建系統(tǒng)表、初始化系統(tǒng)配置等。DDL是指創(chuàng)建表、創(chuàng)建索引、刪除表等動作,DDL不會被優(yōu)化器處理,而是作為命令直接發(fā)送到RS,DDL產(chǎn)生的模式變更保存于系統(tǒng)表并更新到內(nèi)存,然后產(chǎn)生新的版本號通知所有在線的OBServer,OBServer再刷新獲得新版本的模式。
系統(tǒng)表
數(shù)據(jù)庫系統(tǒng)的正常運轉(zhuǎn)離不開元數(shù)據(jù)(Meta Data),例如表的模式(結(jié)構(gòu))信息、系統(tǒng)中數(shù)據(jù)的統(tǒng)計信息、系統(tǒng)的運行狀態(tài)信息等,OceanBase當(dāng)然也不例外。OceanBase中將元數(shù)據(jù)也按照表的形式進行組織和管理,這些存放元數(shù)據(jù)的表被稱為系統(tǒng)表(也被稱為內(nèi)部表)。系統(tǒng)表本質(zhì)上也和普通的用戶表一樣,可以通過SQL語句進行增刪改查等操作,但系統(tǒng)表的操作只能由特殊的通道完成。
系統(tǒng)租戶擁有所有的系統(tǒng)表,在4.0版本中,引入了meta租戶,每一個user租戶都會有一個對應(yīng)的meta租戶,meta租戶也擁有系統(tǒng)表,用于管理元數(shù)據(jù)。為了方便對元數(shù)據(jù)的查看,OceanBase還提供了一些比較復(fù)雜的只讀視圖,它們被稱為“虛擬表”,其名稱以“all_virtual”或者“tenant_virtual”為前綴。
核心系統(tǒng)表
在眾多的系統(tǒng)表中,有一類“一等公民”,它們是其他系統(tǒng)表能夠存在的前提,因此可以稱為核心系統(tǒng)表。由于系統(tǒng)表本身也需要有元數(shù)據(jù)來描述其結(jié)構(gòu),因此存放表結(jié)構(gòu)信息的系統(tǒng)表地位自然會超然于普通系統(tǒng)表之上。
__all_core_table記載著系統(tǒng)中核心系統(tǒng)表的元數(shù)據(jù),這些信息是RootService啟動所需的必要信息
__all_root_table記載了表的分區(qū)和副本信息
all_table中記載著所有表(all_core_table、all_root_table、all_table本身不包括在內(nèi))的表級元數(shù)據(jù)
一個表的元數(shù)據(jù)不僅僅是表自身的描述信息,還應(yīng)包括表中各列的描述數(shù)據(jù),這些數(shù)據(jù)存放在系統(tǒng)表all_column中,表中的每一列在all_column中都有一行
__all_database存放著租戶中所有方案(Schema)的元數(shù)據(jù),每一個方案對應(yīng)著其中的一行
__all_tablegroup中存儲著所有的表組(Table Group)信息,系統(tǒng)中每個表組對應(yīng)其中一行
__all_tenant中存儲著所有的租戶信息,系統(tǒng)中每個租戶對應(yīng)其中一行
__all_ddl_operation中收集著所有執(zhí)行過的DDL操作的信息,該表的schema_version是主鍵,這是因為每一次DDL操作都會導(dǎo)致整個集群的模式中發(fā)生或多或少的改變(例如列結(jié)構(gòu)或表結(jié)構(gòu)改變),為了讓系統(tǒng)中不同時間開始的操作能使用到合適的模式信息,OceanBase每次成功執(zhí)行DDL操作后都會將模式版本增加,因此可以認為每一個DDL操作都有一個唯一的模式版本(Schema Version)
系統(tǒng)表初始化
一個OceanBase集群第一次被啟動時,需要首先進行自舉操作(Bootstrap)形成初始的系統(tǒng)表結(jié)構(gòu)并且將集群中各個服務(wù)器節(jié)點加入到集群之中,通常這一動作是由OBD發(fā)起。OBD在啟動集群時會通過檢查節(jié)點數(shù)據(jù)目錄的clog子目錄是否存在來判斷是否需要進行自舉動作,如果需要進行自舉,則OBD會向集群發(fā)送一系列的SQL命令完成自舉,也就是前文提到的bootstrap操作:
多版本模式服務(wù)
作為多節(jié)點構(gòu)成的分布式數(shù)據(jù)庫系統(tǒng),OceanBase集群中每一個節(jié)點上的操作都需要訪問模式數(shù)據(jù),為了更好地服務(wù)各節(jié)點上的操作,OceanBase基于模式的相對穩(wěn)定性設(shè)計了一套多副本的模式管理方案:各節(jié)點上都緩存有模式數(shù)據(jù)的副本,但對于模式的修改則由RootService所在的節(jié)點實施,在完成模式修改之后由RootService將新的模式版本通知其他節(jié)點,它們將會刷新各自的模式緩存。
為了便于系統(tǒng)中其他模塊使用模式信息,OceanBase基于節(jié)點上的模式副本包裝了一套模式服務(wù)來為其他模塊服務(wù)。由于系統(tǒng)運行中會由于DDL操作導(dǎo)致模式版本發(fā)生變化,不同時刻開始的操作(事務(wù))將會看到(需要)不同版本的模式信息,這套模式服務(wù)準確來說應(yīng)該被稱為“多版本模式服務(wù)”。
OceanBase的多版本模式服務(wù)被實現(xiàn)為ObMultiVersionSchemaService類,在ObServer對象初始化過程中會調(diào)用ObServer::init_schema()方法初始化一個ObMultiVersionSchemaService實例并置于ObServer實例的schema_service_屬性中。
多版本模式服務(wù)為系統(tǒng)中其他模塊提供元數(shù)據(jù)服務(wù),其他模塊可以從模式服務(wù)獲得兩種形態(tài)的模式:
1)完整模式(Full Schema):包含數(shù)據(jù)庫對象的完整模式信息,由名為“Ob###Schema”的類表達,其中###是數(shù)據(jù)庫對象的類型名,例如圖4.3中的ObDatabaseSchema表示數(shù)據(jù)庫(Database)的模式。
2)簡單模式(Simple Schema):僅包括完整模式中的核心部分,由名為“ObSimple###Schema”的類表達。從圖4.3可以看到,簡單模式中僅保留了數(shù)據(jù)庫對象全局性或者關(guān)鍵性的信息(如數(shù)據(jù)庫的ID、名稱),而更細致的如字符集、排序規(guī)則等信息則僅保留在完整模式中。
OceanBase僅將模式信息中最核心的部分(即簡單模式)常駐在緩存中,完整版本的模式信息根據(jù)需要載入非常駐內(nèi)存,當(dāng)內(nèi)存不足時完整模式會被自動淘汰。
多版本模式服務(wù)提供了ObSchemaGetterGuard類作為節(jié)點上的其他模塊訪問模式數(shù)據(jù)的入口。利用ObSchemaGetterGuard獲得的數(shù)據(jù)庫對象的模式信息都是形如ObDatabaseSchema的對象,它們本質(zhì)上是多版本模式服務(wù)管轄的模式緩存中某個版本的緩存數(shù)據(jù)。因此,外部模塊不會孤立地修改緩存中的模式,而是在通過DDL語句修改數(shù)據(jù)庫對象時同步地在模式緩存中產(chǎn)生新版本的模式。不過,這種動作只會發(fā)生在RootService所在的節(jié)點上,其他節(jié)點上需要通過模式刷新才能獲得新版本的模式。
為什么需要多版本schema
DDL可以看作mvcc的寫操作,DML則可以看作mvcc的讀操作
DDL服務(wù)
為了實現(xiàn)對模式的修改,OceanBase在多版本模式服務(wù)中提供了ObSchemaService類作為DDL命令操縱模式的接口。ObSchemaService是一個接口類,目前它僅有一個實現(xiàn):ObSchemaServiceSQLImpl類。
ObSchemaServiceSQLImpl的作用是根據(jù)外部模塊的調(diào)用,返回操縱相應(yīng)數(shù)據(jù)庫對象的SQL服務(wù)類(ObDDLSqlService的子類,如下圖中的ObDatabaseSqlService)的對象,外部模塊再利用SQL服務(wù)對象的方法完成DDL操作。
下圖給出了CREATE DATABASE語句執(zhí)行過程中涉及的DDL服務(wù)部分:主RootService所在節(jié)點收到創(chuàng)建數(shù)據(jù)庫的RP C請求之后將會交給其create_database方法處理,其中關(guān)于模式部分的處理最終會進入多版本模式服務(wù)的ObSchemaService(實際是一個ObSchemaServiceSQLImpl實例)中,進而通過get_database_sql_service方法取得數(shù)據(jù)庫對象的DDL服務(wù)對象(ObDatabaseSqlService),最后調(diào)用其insert_database方法將新建數(shù)據(jù)庫的模式信息[插圖]加入多版本模式服務(wù)管轄的模式緩存中。
模式緩存
為了避免反復(fù)地從持久化存儲中讀出系統(tǒng)表數(shù)據(jù),OceanBase在多版本模式服務(wù)中設(shè)置了模式緩存。由于OceanBase的分布式數(shù)據(jù)庫特性,集群中每個節(jié)點上都會有訪問模式數(shù)據(jù)的需求,因此每個節(jié)點上都有自己的模式緩存。雖然多個節(jié)點上的模式緩存形成了多副本,但整個集群中只有RootService節(jié)點才能通過執(zhí)行DDL語句修改模式信息,這些緩存之間實際是一主多從的關(guān)系,非RootService節(jié)點上的緩存會隨著RootService節(jié)點的緩存變化而刷新,因此不會出現(xiàn)緩存不一致的問題。
模式緩存分為兩部分,第一部分是由ObSchemaCache描述的完整模式緩存,ObSchemaCache用于管理通過ObSchemaGetterGuard產(chǎn)生的完整模式,通過ObSchemaGetterGuard取完整模式時會優(yōu)先在ObSchemaCache中查找,如果能命中則直接從緩存中返回完整模式,否則會構(gòu)造SQL從系統(tǒng)表中讀取元組并構(gòu)造所需的完整模式。在ObSchemaCache內(nèi)部又分為兩個組成部分:
1)sys_cache_:是一個ObHashMap類型的Hash表,顧名思義,sys_cache_中緩存著系統(tǒng)級的模式數(shù)據(jù),例如核心表、系統(tǒng)表等的模式。sys_cache_中緩存的信息一直常駐內(nèi)存,不會因為存儲空間而被替換出緩存。
2)cache_:是一個KVCache類型的KV存儲,它管理著非核心數(shù)據(jù)庫對象的模式,或者說sys_cache_以外的所有模式數(shù)據(jù)都被緩存在cache_中。與sys_cache_不同的是,cache_中的模式數(shù)據(jù)有可能因為存儲空間不足的原因被換出緩存。
在ObSchemaCache中,每一個模式都由一個ObSchemaCacheKey唯一標識,其中包括了模式類型(schema_type_)、模式ID(schema_id_)以及模式版本(schema_version_)。被緩存的對象是ObSchema,它根據(jù)模式類型可以具體化為ObTenantSchema、ObDatabase-Schema等。
模式緩存的第二大組成部分由ObSchemaMgrCache表達,ObSchemaMgrCache中緩存著ObSchemaMgr對象,相較于緩存在ObSchemaCache中的模式對象,ObSchemaMgr更像是從另一個“視角”對模式數(shù)據(jù)的組織,一個ObSchemaMgr對象可以被看成是一個特定租戶在特定模式版本下的模式數(shù)據(jù),而ObSchemaCache則是按照數(shù)據(jù)庫對象來組織模式數(shù)據(jù)。ObSchemaMgr中僅緩存簡單版本的模式。
ObSchemaMgrCache中對ObSchemaMgr對象的管理相對簡單粗放:采用了一個ObSche-maMgrItem數(shù)組(schema_mgr_items_屬性)管理ObSchemaMgr對象及其引用計數(shù)。盡管Ob-SchemaMgrCache初始化時傳入的參數(shù)指定了schema_mgr_items_中緩存的ObSchemaMgr對象的最大數(shù)量,但該數(shù)組實際仍然按最大硬上限(8192個元素,由常量屬性MAX_SCHEMA_SLOT_NUM定義)分配空間決定。因此ObSchemaMgrCache中緩存的ObSchemaMgr數(shù)量有限,發(fā)生緩存替換時會將引用數(shù)為零的ObSchemaMgr對象換出。ObSchemaMgrItem中的引用數(shù)記錄著ObSchemaMgr被使用的次數(shù),通過ObSchemaGetterGuard獲取模式數(shù)據(jù)時若能從ObSchemaMgrCache找到對應(yīng)版本的ObSchemaMgr,則會加引用計數(shù),用完ObSchemaGetterG-uard析構(gòu)時反向減少引用計數(shù),其目的是確保使用ObSchemaGetterGuard期間對應(yīng)的ObSche-maMgr不會被淘汰。
模式刷新
OceanBase數(shù)據(jù)庫集群中各個節(jié)點上都有自己的模式緩存,當(dāng)主RootService節(jié)點上執(zhí)行DDL操作修改模式之后,其他節(jié)點上的模式緩存需要在適當(dāng)?shù)臅r機進行刷新。模式緩存的刷新主要分為主動刷新和被動刷新。
主動刷新
RootServer執(zhí)行完DDL操作并且更新自身的模式緩存時,會產(chǎn)生新的模式版本號。模式版本號可以看成是一種流水號,新的模式版本號是從前一個版本號加1形成。產(chǎn)生新的模式版本號之后,RootServer并不采用廣播的方式通知其他節(jié)點,而是等待其他節(jié)點報告心跳(續(xù)租)時隨著響應(yīng)信息返回給這些節(jié)點。如圖4.8所示(箭頭上的數(shù)字代表步驟序號),集群中的每一個節(jié)點上的OBServer都會定期向RootServer上的主RootService發(fā)送RPC請求更新租約(同時也充當(dāng)心跳包),RootService中的renew_lease方法會處理續(xù)租請求。在完成對該節(jié)點的狀態(tài)更新之后,renew_lease方法會向發(fā)起請求的OBServer返回一個響應(yīng)包LeaseResponse,響應(yīng)包中包含有RootServer上的最新模式版本號。OBServer收到租約響應(yīng)包之后會由ObHeartBeatProcess的do_heartbeat_event方法處理,其中會根據(jù)租約響應(yīng)包的最新模式版本號嘗試調(diào)用ObServerSchemaUpdater::try_reload_schema()進行模式重載,即將刷新任務(wù)包裝成一個類型為REFRESH的ObServerSchemaTask任務(wù)放入任務(wù)隊列中等待處理線程異步執(zhí)行,而模式的刷新最終會被路由到該節(jié)點的多版本模式服務(wù)的refresh_and_add_schema方法中。
模式的主動刷新僅為所在節(jié)點載入新版本的SchemaMgr,即簡單形式的模式。而完整模式的刷新則要依賴被動刷新方式。
被動刷新
當(dāng)其他模塊想要獲取完整模式(會指定其模式版本)時,如果所在節(jié)點模式緩存中無法找到對應(yīng)版本的完整模式,會實時觸發(fā)SQL從系統(tǒng)表構(gòu)造指定版本的完整模式,并放入到當(dāng)前節(jié)點的模式緩存中。嚴格來說,上述模式刷新方式其實并不符合“刷新”一詞的語義,因為OceanBase采用的是多版本并發(fā)控制,模式的變動并不是通過“就地”(In-place)修改的方式體現(xiàn),而是形成一個新版本的完整模式。因此,所謂的模式“刷新”實際是將之前沒有訪問過的其他版本的模式加入到模式緩存中。兩種不同形式的模式中,由于簡單模式的使用會更加頻繁,因此對簡單模式的“刷新”采用更為激進的主動刷新方式。