淘寶數據庫OceanBase SQL編譯器部分 源碼閱讀--Schema模式
淘寶數據庫OceanBase SQL編譯器部分 源碼閱讀--Schema模式
什麼是Database,什麼是Schema,什麼是Table,什麼是列,什麼是行,什麼是User?我們可以可以把Database看作是一個大倉庫,倉庫分了很多很多的房間,Schema就是其中的房間,一個Schema代表一個房間,Table可以看作是每個Schema中的櫃子,行和列就是櫃子中的格子。User就是房間的主人。簡單來說,Schema是包括表,列,索引,視圖等數據庫對象的集合。
OceanBase中的強Schema
OceanBase要求schema強類型約束,也就是要預先定義好schema。這與傳統的數據庫基本相同,與很多nosql的schema-free大相徑庭。
也就是說,OceanBase的數據模式是基於關係模型的。關係模型通過關係分解消除數據結構的複雜性,獲得了對數據查詢的能力和靈活性。而關係模型的缺點也是因為關係分解,使得在需要“組裝”的數據時要進行join操作,而join則是相當耗時的操作。
大部分的NoSQl產品以MongoDb為代表,是基於文檔模型的,類似json的bjson格式,因此可以存儲比較複雜的數據類型,並且可以避免了join操作。在獲得數據結構的可擴展性的同時,則失去了對通用數據查詢語言(SQL)的支持。你需要重新學習這些NoSQL產品的查詢語言。
關於關係數據庫與NoSQL的對比,這裏有一篇好文推薦給大家:
OceanBase中Schema的格式
根據OceanBase官方文檔的介紹:
OceanBase 中的schema 表示為純文本的ASCII碼文件, 采用常見的配置文件的形式. 分成各個 section. 每個section 下有多個配置項, 配置項名稱和配置值之間用”=”連接.
OceanBase 中 schema 是以應用為單位的, 一個應用一個schema文件. 一個應用中可以包含多張表, 每張表中可以包含多個列, 以及多個聯表(join)關係.
我們以下麵一個例子test1.ini
文件來逐個梳理Schema的各個部分。文件內容如下:
[app_name] name=collect max_table_id=1003 [u_collect_item_id] table_id=1001 table_type=1 column_info=1,2,item_name,int column_info=1,3,new_price,varchar,20 rowkey_split=0 rowkey_max_length=9 max_column_id=3 [collect_info] table_id=1002 table_type=2 column_info=1,2,item_name,int column_info=1,3,item_price,varchar,20 rowkey_split=8 join=rowkey[8,16]%u_collect_item_id:item_name$item_name,item_price$new_price rowkey_max_length=17 max_column_id=3 [collect_item_id] table_id=1003 table_type=2 column_info=0,2,item_name,varchar,20 column_info=0,3,new_price,int rowkey_split=0 rowkey_max_length=9 max_column_id=3
應用的信息
[app_name]
section為應用的信息。其他section每一個代表一張表。
目前主要有兩個配置項: name
:用來配置應用的名稱, 是一個長度不超過128位的字符串.max_table_id
:用來記錄當前已經使用的最大的table_id.
在OceanBase中, 每個表都由table_id唯一標識, 且table_id不可以被重複使用. max_table_id 這個配置項, 主要是為了方便 schema 生成程序記錄已經使用過的table_id.
表的信息
Schema定義文件中,除了[app_name]
外的其他section都是一張表的信息。
section名就是表名,因此上麵文件中定義了3張表u_collect_item_id
,collect_info
,collect_item_id
.table_id
:配置項配置了這張表在OceanBase係統中的唯一id,
由schema 生成工具自動生成. 在OceanBase係統中, id的取值範圍是0-65535.係統會保留0-1000的table_id供係統自身使用.
table_type
:用來配置表是內存表還是磁盤表. 因為一張表的動態數據存儲在UpdateServer上,動態部分不受table_type影響,
所以table_type實際上表示了ChunkServer上的靜態部分是放到內存中還是放到磁盤上. 該配置項取值為 1 的時候, 表示靜態部分放到磁盤上, 為2的時候, 表示靜態部分放到內存中.
rowkey_max_length
:用來配置表中主鍵的最大長度.
rowkey_split
:配置表在存儲的時候的拆分限製.
因為一個表的數據可能放到多個tablet上存儲, 這個值告訴ChunkServer, 在分裂數據到不同tablet時哪些數據是不應該被分開的, 比如, 當這個值為9的時候, 表示主鍵前9個字節完全相同的記錄不應該被分到兩個不同的tablet中.
max_column_id
:配置本表中已經使用過的最大的列id,
由schema 生成程序維護並使用, 防止對列id的重用.
compress_func_name
:可選項, 配置這個表在存儲時使用的壓縮算法名字.
block_size
:可選項, 配置表在存儲成sstable時,采用的block大小.
use_bloomfilter
:可選項, 配置表是否使用布隆過濾器,
非零值為使用.
rowkey_is_fixed_length
:可選項, 配置主鍵是否是固定長度值.
非零值表示主鍵是固定長度的. 如果不配置該項, 默認主鍵為固定長度.
列的配置
column_info
:配置項中的內容是具體描述一列的,
用”,”分開, 其內容包含列屬性, 列id, 列名, 列類型.
-
列的屬性:取值為0或者1. 為0表示該列隻有動態數據(隻存在於UpdateServer); 為1表示該列既有動態數據又有靜態數據(既存在於UpdateServer 又存在於 ChunkServer).
-
列id:是這個列在表中的唯一標識, 該值由schema生成程序生成, 不可以被重用. 列id必須大於1, 係統保留id為1的用於表示主鍵.
-
列名:是一個長度不超過128位的字符串.
-
類型:列的數據類型.
所以例子中的 column_info=1,3,item_price,varchar,20
表示 : 列名字是 item_price; id 是3; 靜態數據放在磁盤上; 類型是 varchar, 長度是20個字符.
因為OceanBase的聯表(join)設計,使得某些隻有動態數據的列是有意義的, 這種列一般在轉儲過程中會通過join運算轉儲到其它表中.
聯表(Join)關係的配置
聯表(join)關係是OceanBase提供的簡化關聯查詢的有力手段. join
:這個配置項裏描述的是join關係的具體內容
我們看一個例子: join=rowkey[8,16]% collect_item_info:item_name$item_name,item_price$new_price
這一行表示當前表的一個join關係. join=rowkey[8,16]%collect_item_info
是表示用當前記錄的主鍵的第8-16字節(閉區間) 與表collect_item_info
進行join操作. “:”後的內容表示具體發生join的列.
用”$”分開的兩個列分別被稱為參與join操作的左列和右列. join操作總是用右列的值合並到左列的值上, 然後將合並的結果返給用戶(左列和右列的值都不發生變化, 合並隻體現在反給用戶的結果中).
所以上述的表達是說, 當訪問當前表的時候, 如果訪問到列
item_name
或者
item_price
. 則需要以當前主鍵的 8-16 字節為主鍵查找表
collect_item_info
. 如果查到記錄, 則用其item_name
列的值與當前記錄的item_name
的值做合並, 用其new_price
列的值與當前記錄的item_price
的值做合並,
將合並的結果作為最終值返給客戶.
以上的格式介紹出自OceanBase的官方文檔 doc/OceanBase的schema.docx
Schema的管理
以下討論基於OceanBase0.3版本。
Schema由RootServer進行管理,包括Schema配置文件合Schema管理器兩部分。RootServer可以通過
switch_schema
和
switch_schema_manager
來切換不同的schema配置文件和不同的schema管理器
ObSchemaManagerV2
。
bool ObRootServer2::get_schema(ObSchemaManagerV2& out_schema) const; /* 從本地讀取新schema, 判斷兼容性 */ int ObRootServer2::switch_schema(int64_t time_stamp, ObArray<uint64_t> &deleted_tables); void ObRootServer2::switch_schema_manager(ObSchemaManagerV2 *schema_manager);
Schema的代碼組織
與配置文件的結構類似,表、列和join聯表分別對應了3個類。
class ObJoinInfo; class ObColumnSchemaV2; class ObTableSchema;
(應用信息[app_name]的相關代碼在哪?)
這三個類主要功能就是對配置文件中的各個屬性進行get和set。各種屬性在上節已經有較詳細的介紹,不再重複。
Schema管理器
Schema管理器ObSchemaManagerV2
負責管理每個Schema中的TableSchema,ColumnSchema,JoinInfo。擁有TableSchema,ColumnSchema,JoinInfo的get和set函數。
Schema管理器ObSchemaManagerV2
對應了一個完整的Schema配置文件。可以從配置文件解析生成相應的TableSchema,ColumnSchema,JoinInfo。一個配置文件中可以配置多對張表,每個表有多個列,體現在數組table_infos_
和columns
上。,此外,還有兩個ObHashMap結構以列Id和列名作為key,用於加速列的查詢。
class ObSchemaManagerV2 { public: /*省略其他方法,成員變量*/ bool parse_from_file(const char* file_name, tbsys::CConfig& config); bool parse_one_table(const char* section_name, tbsys::CConfig& config, ObTableSchema& schema); bool parse_column_info(const char* section_name, tbsys::CConfig& config, ObTableSchema& schema); bool parse_join_info(const char* section_name, tbsys::CConfig& config, ObTableSchema& schema); bool parse_expire_info(const char* section_name, tbsys::CConfig& config, ObTableSchema& schema); }; private: char app_name_[OB_MAX_APP_NAME_LENGTH]; ObTableSchema table_infos_[OB_MAX_TABLE_NUMBER]; ObColumnSchemaV2* columns_; hash::ObHashMap<ObColumnNameKey,ObColumnInfo,hash::NoPthreadDefendMode> column_hash_map_; hash::ObHashMap<ObColumnIdKey,ObColumnInfo,hash::NoPthreadDefendMode> id_hash_map_; int64_t join_table_nums_; uint64_t join_tables_[OB_MAX_TABLE_NUMBER];
其他涉及Schema管理的模塊
在OceanBase係統中,用戶的讀寫事務都會發給MergeServer。MergeServer解析這些讀寫事務的內容,例如詞法和語法分析、schema檢查等。對於隻讀事務,由MergeServer發給相應的ChunkServer分別執行後再合並每個ChunkServer的執行結果;對於讀寫事務,由MergeServer進行預處理後,發送給UpdateServer執行。
因此在MergeServer中也存在一個Schema管理的接口ObMergerSchemaManage
.
ObMergerSchemaManager
管理SchemaManager
,可以獲取,添加和釋放ObSchemaManagerV2
,schema
manager 最多有
MAX_VERSION_COUNT
(默認為4)個
SchemaManager
的實例。我們可以通過get_schema
來獲取Schema管理器ObSchemaManagerV2
。還可以通過add_schema
,release_schema
來添加或釋放ObSchemaManagerV2
。如果實例到達上限時繼續添加,則會刪除最舊的一個實例,然後在添加新的SchemaManager
實例。
const ObSchemaManagerV2 * get_schema(const ObString & table_name); const ObSchemaManagerV2 * get_schema(const uint64_t table_id); int add_schema(const ObSchemaManagerV2 & schema, const ObSchemaManagerV2 ** manager = NULL); int release_schema(const ObSchemaManagerV2 * schema);
總結
與傳統數據庫類似,OceanBase要預先定義schema。采用ASCII配置文件對Schema進行配置。一個應用使用一個schema文件. 可以包含多張表, 每張表中可以包含多個列, 以及多個聯表(join)關係.RootServer負責管理Schema配置和Schema管理器。
歡迎光臨我的網站----蝴蝶忽然的博客園----人既無名的專欄。
如果閱讀本文過程中有任何問題,請聯係作者,轉載請注明出處!
最後更新:2017-04-03 07:57:10