衆所周知,由于mysql采用統一server層+不同的底層引擎插件的架構模式,在server層為每個表建立了frm檔案,以儲存與表定義相關的中繼資料資訊。然而某些引擎(例如innodb)本身也會存儲中繼資料,這樣不僅産生了中繼資料備援,而且由于server層和引擎層分别各自管理,在執行ddl之類的操作時,很難做到crash-safe,更别說讓ddl具備事務性了。
為了解決這些問題(尤其是ddl無法做到atomic),從mysql8.0開始取消了frm檔案及其他server層的中繼資料檔案(frm, par, trn, trg, isl,db.opt),所有的中繼資料都用innodb引擎進行存儲, 另外一些諸如權限表之類的系統表也改用innodb引擎。
本文是筆者初次了解這塊内容,是以不會過多深入,由于涉及的改動太多,後面有空再逐個展開。
本文所有測試和代碼相關部分都是基于mysql8.0.0版本,由于這是8.0大版本的第一個開發版本,不排除未來行為會發生變化。
首先我們建立一個新庫,并在庫下建立兩個表來開啟我們的測試
可以看到在庫目錄下隻有ibd檔案,并沒有frm檔案,而在資料目錄下,相應的生成了一個sdi檔案,來描述這個sbtest庫的資訊。
我們再來看看建立一個myisam引擎的表:
這裡我們建立了一個myisam表t1,相應的一個sdi檔案被建立,檔案中以json的格式記錄了該表的詳細資訊。根據官方檔案的描述,這個檔案的存在是為了一個還未完全實作的功能。
一些新is表使用view進行了重新設計,主要包括這些表:
也就是說,雖然dd系統表被隐藏不可見了,但你依然可以通過視圖獲得大部分資訊。這種方式實際上大大加快了is表的查詢速度,轉換成實體表的查詢後,将無需為每個is表的查詢建立臨時表(臨時表的操作包含了server層建立frm, 引擎層擷取資料or需要鎖保護的全局資料)。另外優化器也能為is表的查詢選擇更好的執行計劃(例如使用系統表上的索引進行查詢)。
官方對此做了測試,結果顯示對is表的查詢性能大幅度提升,官方部落格傳送門:
<a href="http://mysqlserverteam.com/mysql-8-0-improvements-to-information_schema/">mysql 8.0: improvements to information_schema</a>
<a href="http://mysqlserverteam.com/mysql-8-0-scaling-and-performance-of-information_schema/">mysql 8.0: scaling and performance of information_schema</a>
資料詞典的結構發生巨大的變化後,相應的對于記憶體資料詞典cache也做改動,
hardcode的字元集cache:
和權限相關的表轉換成innodb引擎
// 包括:user, db, tables_priv, columns_priv, procs_priv, proxies_priv
func表轉換成innodb事務表
// 基于此變化,對function的操作(例如create function或者drop function, 或者使用者定義的udf)可能會導緻一次隐式送出
mysql庫下的routine表及event表不再使用,這些資訊被存儲到新的dd table中,并且在mysql庫下是不可見的。
外鍵系統表
// 使用兩個不可見的系統表foreign_keys和foreign_key_column_usage來存儲外鍵資訊
// 由于這兩個系統表不可見,你需要通過is庫下的referential_constraints和key_column_usage表來獲得外鍵資訊
// 引入的不相容:foreign key的名字不可以超過64個字元(之前版本是允許的)
我們回到源代碼目錄下,大量的新代碼檔案被引入,以從server層管理new dd,主要定義了一系列統一的api,代碼存于sql/dd目錄下,函數和類定義在namespace dd下
針對不同的中繼資料分别定義了不同的類及其繼承關系:
資料詞典cache管理類:
mysql庫存儲的是系統表,但通過show tables指令,我們隻能看到37個表,而從磁盤來看mysql目錄下ibd檔案遠遠超過37個,這意味着有些系統表對使用者是不可見的,這些表也是用于管理核心資料詞典資訊,不可見的原因是避免使用者不恰當的操作。(當然也不排除未來這一行為發生變化),關于這些表的通路,在目錄<code>sql/dd/impl/tables/</code>中進行了接口定義,這些隐藏的表包括:
我們以對一個表的常見操作為例,看看其中一些代碼是如何被調用的。
(由于new dd的代碼改動很大,相關的worklog有幾十個,筆者通過測試+代碼debug的方式第一步先熟悉代碼,記錄的比較淩亂)
建立database
入口函數:<code>mysql_create_db</code>
-- 建立database目錄
-- 建構binlog并寫入檔案
-- 調用dd api接口: dd::create_schema
修改database
入口函數: <code>mysql_alter_db</code>
-- 調用dd api接口: <code>dd::alter_schema</code>
-- 寫binlog
show databases
執行該指令時,實際上會對其進行一個sql轉換,将其轉換成一個标準的查詢語句,堆棧如下:
轉換後的sql類似:
由于直接從系統表中讀取, 這意味着在資料目錄下建立一個檔案夾将不會當作新的資料庫目錄。
删除database
-- 删除相關檔案
-- 删除系統表mysql/schemata中記錄
建立表
入口函數:
-- 先在dd中插入新的記錄(<code>dd::create_table</code> --> <code>dd::create_dd_user_table</code>)
-- 然後再建立引擎檔案
open table
-- 将執行個體重新開機後,然後再打開表,表定義第一次載入記憶體,需要先去通路系統表拿到表定義:
wl#6379: schema definitions for new dd
wl#6380: formulate framework for api for dd
wl#6381: handler api changes for new dictionary
wl#6382: define and implement api for table objects
wl#6383: define and implement api for triggers
wl#6384: define and implement api for stored routines
wl#6385: define and implement api for schema
wl#6387: define and implement api for tablespaces
wl#6388: define and implement api for events
wl#6389: define and implement api for views
wl#6390: use new dd api for handling non-partitioned tables
wl#6391: protect data dictionary tables
wl#6392: upgrade to transactional data dictionary
wl#6394: bootstrap code for new dd
wl#6416: innodb: remove the use of *.isl files
wl#6599: new data dictionary and i_s integration
wl#6929: move foreign key constraints to the global data dictionary
wl#7053: innodb: provide storage for tablespace dictionary
wl#7066: external tool to extract innodb tablespace dictionary information
wl#7069: provide data dictionary information in serialized form
wl#7167: change ddl to update rows for view columns in dd.columns and other dependent values.
wl#7284: implement common code for different dd apis
wl#7464: innodb: provide a way to do non-locking reads
wl#7488: innodb startup refactoring
wl#7630: define and implement api for table partition info
wl#7771: make sure errors are properly handled in dd api
wl#7784: store temporary table metadata in memory
wl#7836: use new dd api for handling partitioned tables
wl#7896: use dd api to work with triggers
wl#7897: use dd api to work with stored routines
wl#7898: use dd api to work with events
wl#7907: runtime: use non-locking reads for dd tables under i_s view.
wl#8150: dictionary object cache
wl#8433: separate dd commands from regular sql queries in the parser grammar
wl#8980: move udf table from myisam to transactional storage
wl#9045: make user management ddls atomic
官方部落格:
<a href="https://mysqlserverteam.com/mysql-server-bootstrapping-and-dictionary-initialization/">https://mysqlserverteam.com/mysql-server-bootstrapping-and-dictionary-initialization/</a>
<a href="https://mysqlserverteam.com/bootstrapping-the-transactional-data-dictionary/">https://mysqlserverteam.com/bootstrapping-the-transactional-data-dictionary/</a>