淘寶數據庫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的對比,這裏有一篇好文推薦給大家:

自由模式的MySQL vs 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_idcollect_infocollect_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管理器。


歡迎光臨我的網站----蝴蝶忽然的博客園----人既無名的專欄
如果閱讀本文過程中有任何問題,請聯繫作者,轉載請註明出處!

發佈了35 篇原創文章 · 獲贊 47 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章