天天看點

詳解MySQL-8.0資料字典

1. 引言

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

詳解MySQL-8.0資料字典

圖1

俗話說知己知彼,方能百戰不殆。在介紹MySQL-8.0的資料字典前,我們先一起回顧一下MySQL-8.0之前的資料字典。

2.1 Data Dictionary 分布位置

詳解MySQL-8.0資料字典

圖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