快人一步,X-DB如何實現極速DDL
X-DB是阿里巴巴自研的分散式關係型資料庫,它不僅具備可全球部署的分散式能力,在效能和成本方面也有可觀提升,並且增加了不少惠及使用者的新功能。本文將詳細介紹X-DB如何近乎瞬時完成SQL/">MySQL等傳統資料需要數小時完成的DDL操作。
1.資料庫DDL操作面臨的問題
網際網路業務發展迅速,應用模式頻繁更改是常態。相應地,資料庫訪問模式和schema也隨之變化。DDL(Data Definition Language)是SQL的一類,主要作用是建立和更改資料的schema資訊,最常見的操作包括:加減列、更改列型別、加減索引等。熟悉MySQL的同學都知道,在8.0以前,雖然Online DDL不阻塞其它DML(Insert/Update/Delete)操作,但許多重要的DDL操作,如加列、減列等,仍舊需要等待數小時(依據資料量的大小)才會生效。更改列型別等操作甚至仍需要鎖表執行,阻塞DML操作。
DDL操作執行時間長,佔用系統資源,需要額外的磁碟空間(建立臨時表),影響系統吞吐,並且一旦DDL過程中例項crash,恢復時間也會很久。以加列DDL為例,MySQL經歷如下過程:
1.以新schema建立空表。
2.拷貝資料到新表,並且將新加列的值賦為預設值,同時更新索引表。資料庫接受到的DML操作被記錄在臨時檔案。
3.加exclusive lock,阻塞寫操作,將臨時檔案記錄的DML操作apply到新表。如果DML很多,這一階段將花費較多時間。
4.刪除舊錶,將新表命名為舊錶的名字。
顯然,這個過程加鎖時間長,拷貝資料操作會佔用系統資源和臨時空間,並需要大量I/O。為了適應變化頻繁的業務,不立即更改儲存層資料、可以快速完成的DDL(我們稱之為Fast DDL)成為了一個必要feature。今年釋出的MySQL 8.0,增加了instant add column功能,可以在短時間內只修改table元資訊,完成加列操作。遺憾的是,它還不支援其它型別的DDL。得益於阿里自研的儲存引擎X-Engine儲存了多版本Table Schema,每一行記錄在引擎層就完成了解析,並且可以依據更新版本schema實現格式轉換,X-DB因此支援多種型別的Fast DDL。
2.業界Fast ddl實現方案
MySQL 8.0
record記錄了列個數, instant add column操作只修改系統表。
寫操作:新格式的記錄。
讀操作:根據儲存在系統表中default value補齊新加列。
支援型別:
- Change index optionRename table
- Set/drop default
- Modify column when the table is empty
- Add/drop virtual columnsAdd columns
MariaDB10.3
整體實現方案與MySQL8.0類似,record記錄了列個數,在leftmost leaf page中記錄所有列的default值.
支援型別:
- Add column
- Drop column
- Extend VARCHAR maximum (Only if the physical format allows; not VARCHAR(255) to VARCHAR(256))
Aurora
發生ddl後,更新系統表,新、舊版本的schema均要記錄下來。然後廣播該修改。之後接受DML請求,首先轉換相關leaf page的所有記錄,然後執行DML。
select請求會將舊版本的記錄拼接成新版本記錄
支援型別:
- only supports adding nullable columns, without default values
3.X-Engine多版本schema
顧名思義,Fast DDL指資料庫能夠在極短的時間內完成使用者發出的DDL指令並返回。之所以這麼快,是因為只修系統表裡的元資料,不變更引擎層儲存的資料。其實現的關鍵在於:元資訊變更之後,記憶體、磁碟中的物理記錄該如何解析。
X-DB基於新一代儲存引擎X-Engine,它的架構採用了LSM-Tree的思想,將新寫入的資料以追加方式寫入記憶體memtable,memtable到一定大小後switch為immutable memtable,不再修改。然後逐漸以extent的形式,flush到持久化儲存中。當extent到一定數量後,通過合併(Compaction)操作,將相同Key的多個版本合併。為了讓每行記錄可解析,最直觀簡單的方案便是將元資訊附著在記錄上面。為了能夠不依賴系統表解析記錄,X-Engine儲存了較為詳細的元資料,如果為每一行都附著一份,會佔用大量的空間。為了減少儲存成本,我們保證每個memtable和extent內部的資料schema一致,並將schema資訊儲存在memtable和extent之上。

schema資訊包含了諸如列個數、列型別、列長度、預設值等關鍵資訊。利用這些資訊,X-Engine可以在返回結果之前,完成列解析,並只需返回查詢目標列的對應結果。下面給出了一個具體的例子,同一張表存在不同schema版本的extent時,如何返回結果。

4.X-DB fast ddl實現
X-DB是一個分散式關係型資料庫,所有元資訊都由名為GMS(Global Management Service)的模組管理。當X-DB接收到一條fast ddl語句時,GMS更新相關元資料表資訊(分散式DDL的執行流程的詳細過程,會在另一篇文章介紹,不是本文重點),新版本的表結構隨之生效,這時這條DDL語句就執行成功啦!到現在為止X-Engine儲存的資訊沒有發生任何變化。
讀請求
當系統接收到Select請求時,X-DB Server會將請求本身,連同當前最新版本schema資訊(稱之為target schema)傳遞到X-Engine。X-Engine首先定位到記錄的位置(某個memtable或extent),並取相應資料schema解析記錄得到初步結果。接著,對比資料schema和target schema,對初步結果做適當填充、刪減或修改得到最終結果返回。
X-Engine schema更新
Fast DDL命令執行成功,新版本的schema生效,X-Engine還對此無感知。當X-DB接收到第一條針對該表的DML(Insert/Update/ Delete)請後,如果發現X-Engine的活躍memtable的schema版本落後於最新版本,會觸發switch memtable行為:凍結當前活躍memtable,產生新活躍memtable,將新schema賦予新活躍memtable。為了保證資料的正確性,該操作會等待所有正在進行的寫事務完成後再執行。
寫請求
每個寫事務可能涉及到n(n>=1)個表。事務在提交時,需要在寫入活躍memtable之前判斷:事務寫入資料的schema版本是否與活躍memtable的schema版本一致,如果不一致則應該報錯退出,提醒使用者重試。
Flush/Compaction
記憶體中memtable數量到一定個數時會觸發Flush操作,被選中memtable的資料以extent的形式寫入磁碟,schema也隨之由memtable傳遞到extent。Compaction操作會合並多個extent,如果參與同一任務的extent schema版本不一致,X-Engine會以其中最新版本為準,生成新extent。
5.X-DB支援的Fast DDL型別
目前X-DB支援的Fast DDL型別包括:
Renaming a table
Renaming an index
Renaming a column
Adding a column
Dropping a column
Reordering columns
Setting a column default value
Dropping the column default value
Changing the column data type, including:
- TINYINT->SMALLINT->MEDIUMINT->INT->BIGINT
- TINYTEXT->TEXT->LONGTEXT
- TINYBLOB->BLOB->LONGBLOB
- VARCHAR<->TEXT , not supported if column size decreased
- VARBINARY<->BLOB, not supported if column size decreased
Extending CHAR/BINARY column size
Extending VARCHAR/VARBINARY column size
Adding a virtual column
Modifying virtual column order
Dropping a virtual column
6.總結
X-DB Fast DDL可以解決很多應用的痛點,加列、擴充套件列的常用的操作不用再需要漫長的等待。技術上,X-Engine通過儲存詳細的多版本schema資訊,不僅無需藉助系統表解析記錄,而且可以輕易地實現不同版本schema之間的資料轉換,進而可以支援型別豐富的Fast DDL。
