1. 引言
資料字典(Data Dictionary)中存儲了諸多資料庫的中繼資料資訊如圖1所示,包括基本Database, table, index, column, function, trigger, procedure,privilege等;以及與存儲引擎相關的中繼資料,如InnoDB的tablespace, table_id, index_id等。MySQL-8.0在資料字典上進行了諸多優化,本文将對其進行逐一介紹。

圖1
俗話說知己知彼,方能百戰不殆。在介紹MySQL-8.0的資料字典前,我們先一起回顧一下MySQL-8.0之前的資料字典。
2.1 Data Dictionary 分布位置
圖2
如圖2所示,舊的資料字典資訊分布在server層,mysql庫下的系統表和InnoDB内部系統表三個地方,其中儲存的資訊分别如下所示:
server層檔案
.frm files: Table metadata files.
.par files: Partition definition files. InnoDB stopped using partition definition files in MySQL 5.7 with the introduction of native partitioning support for InnoDB tables.
.TRN files: Trigger namespace files.
.TRG files: Trigger parameter files.
.isl files: InnoDB Symbolic Link files containing the location of file-per-table tablespace files created outside of the data directory.
.db.opt files: Database configuration files. These files, one per database directory, contained database default character set attributes.
mysql庫下的系統表
mysql.user mysql.db mysql.proc mysql.event等
show tables in mysql;
InnoDB内部系統表
SYS_DATAFILES
SYS_FOREIGN
SYS_FOREIGN_COLS
SYS_TABLESPACES
SYS_VIRTUAL
資料字典分散存儲,維護管理沒有統一接口
MyISAM系統表易損壞
DDL沒有原子性,server層與innodb層資料字典容易不一緻
檔案存儲資料字典擴充性不好
通過information_schema查詢資料字典時生成臨時表不友好
鑒于舊資料字典的種種缺點,MySQL-8.0對資料字典進行了較大的改動:把所有的中繼資料資訊都存儲在InnoDB dictionary table中,并且存儲在單獨的表空間mysql.ibd裡,其架構如圖3所示。下面逐一介紹各項改變的細節。
圖3
MySQL下的原有系統表由MyISAM轉為了InnoDB表,沒有了proc和event表,直接改存到了dictionary table中。在debug模式下,可用如下指令檢視dictionary tables:
資料字典表資訊可以通過全局的cache進行緩存。
table_definition_cache:存儲表定義
schema_definition_cache: 存儲schema定義
stored_program_definition_cache:存儲proc和func定義
tablespace_definition_cache: 存儲tablespace定義
另外還有character,collation, event, column_statistics也有cache,不過其大小寫死不可配置:
圖4
information_schema的變化如圖4所示,主要包括以下幾個方面:
1. information_schema部分表名變化
Old Name
New Name
INNODB_SYS_COLUMNS
INNODB_COLUMNS
INNODB_SYS_DATAFILES
INNODB_DATAFILES
INNODB_SYS_FIELDS
INNODB_FIELDS
INNODB_SYS_FOREIGN
INNODB_FOREIGN
INNODB_SYS_FOREIGN_COLS
INNODB_FOREIGN_COLS
INNODB_SYS_INDEXES
INNODB_INDEXES
INNODB_SYS_TABLES
INNODB_TABLES
INNODB_SYS_TABLESPACES
INNODB_TABLESPACES
INNODB_SYS_TABLESTATS
INNODB_TABLESTATS
INNODB_SYS_VIRTUAL
INNODB_VIRTUAL
2. 通過information_schema查詢時不再需要生成臨時表擷取,而是直接從資料字典表擷取
3. 不需要像以前一樣掃描檔案夾擷取資料庫清單,不需要打開frm檔案擷取表資訊,而是直接從資料字典表擷取
4. information_schema查詢以view的形式展現,更利于優化器優化查詢
1. 去掉server層的中繼資料檔案,中繼資料統一存儲到InnoDB資料字典表,易于管理且crash-safe
2. 支援原子DDL
3. information_schema查詢更高效
MySQL8.0不僅将中繼資料資訊存儲在資料字典表中,同時也備援存儲了一份在SDI中。對于非InnoDB表,SDI資料在字尾為.sdi的檔案中,而對于innodb,SDI資料則直接存儲與ibd中,如以下例子所示:
上述例子中MyISAM表t2的SDI為test/t2_337.sdi,其中337為table_id, t2_337.sdi可以直接打開,資料是json格式(cat test/t2_337.sdi):
SDI在ibd中實際是以表(BTree)的形式存儲的。建表時會通過btr_sdi_create_index建立SDI的BTree,同時會向BTree插入table和tablespace的SDI資訊,表的結構如下:
[INFO] ibd2sdi: SDI is empty.
import (import table *.sdi)隻支援MyISAM表,InnoDB不支援。由于SDI不包含trigger資訊,是以import也不會導入trigger資訊,trigger需額外處理。
例如create table 會涉及到mysql.tablespaces,mysql.tablespace_files, mysql.tables, mysql.indexes, mysql.columns,mysql.index_column_usage等。create table的過程如圖5所示:
圖5
下面以表t1為例,示範create table在DD中的資料分布:
drop table是create table的逆過程,不再具體分析。
圖6
mysqld --initialize的源碼流程如圖6所示。具體過程為:
data dictionary
storage engine
binary log
隻有InnoDB engine支援Atomic DDL,以下操作不支援:
Table-related DDL statements that involve a storage engine other than InnoDB.
INSTALL PLUGIN and UNINSTALL PLUGIN statements.
INSTALL COMPONENT and UNINSTALL COMPONENT statements.
CREATE SERVER, ALTER SERVER, and DROP SERVER statements.
将DDL分為以下幾個階段, Prepare記錄DDL log,Post-DDL會replay log來送出或復原DDL操作,同時也并清理DDL log。
如果DDL成功commit,在post-DDL階段,DDL log記錄被清理了,不需要replay。如果DDL失敗rollback,在post-DDL階段,DDL log清理操作也復原了,需要replay, relay會rollback前面的建立ibd,BTree,以及修改DD share cache。
如果create table過程中發生crash, 重新開機後會讀取ddl log完成ddl的復原。
8. Persistent AutoincMySQL8.0以前自增值沒有持久化,重新開機時通過select MAX(id)的方式擷取目前自增值,這種方式自增值會重複利用。MySQL8.0開始支援自增值持久化,通過增加redo日志和Data Dictonary 表mysql.innodb_dynamic_metadata來實作持久化。每次insert/update更新自增值時會将自增值寫到redo日志中,參考dict_table_autoinc_log函數,日志格式如下:
同時dict_table_t增加了新的變量autoinc_persisted, 在每次checkpoint時會将autoinc_persisted存儲到表mysql.innodb_dynamic_metadata中。
dict_table從dictionary cache淘汰時也會将autoinc_persisted持久化到mysql.innodb_dynamic_metadata中。
crash重新開機時,先從mysql.innodb_dynamic_metadata擷取持久化的自增值,再從redo日志中讀取最新的自增值, 參考MetadataRecover::parseMetadataLog,并通過MetadataRecover::apply更新到table->autoinc。
原mysql5.7 mysql庫下不能存在dictinary table同名的表
不支援老版本(5.6之前)的資料類型decimal,varchar, data/datetime/timestamp, 通過check table xxx for upgrade可以檢測
non-native 分區表不支援
不支援5.0之前的trigger,5.0之前的trigger沒有definer
foreign key constraint name 不能超過64位元組
view的column不能超過255 chars
enum 類型不能超過255 chars.
frm需與InnoDB系統表一緻
一些空間函數如PointFromText需修改為ST_POINTFROMTEXT