MySQL-8.0 | 数据字典最强解读

1. 引言 数据字典(Data Dictionary)中存储了诸多数据库的元数据信息如图1所示,包括基本Database, table, index, column, function, trigger, procedure,privilege等;以及与存储引擎相关的元...

1. 引言

数据字典(Data Dictionary)中存储了诸多数据库的元数据信息如图1所示,包括基本Database, table, index, column, function, trigger, procedure,privilege等;以及与存储引擎相关的元数据,如InnoDB的tablespace, table_id, index_id等。MySQL-8.0在数据字典上进行了诸多优化,本文将对其进行逐一介绍。

attachments-2019-12-0DG7Us5r5df234739fb5c.jpg

2. MySQL-8.0之前的数据字典

俗话说知己知彼,方能百战不殆。在介绍MySQL-8.0的数据字典前,我们先一起回顾一下MySQL-8.0之前的数据字典。

2.1 Data Dictionary 分布位置

attachments-2019-12-DjFa4HsE5df23492a25ea.jpg

                                                                       图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

2.2 存在的问题

数据字典分散存储,维护管理没有统一接口

MyISAM系统表易损坏

DDL没有原子性,server层与innodb层数据字典容易不一致

文件存储数据字典扩展性不好

通过information_schema查询数据字典时生成临时表不友好

3. MySQL-8.0的数据字典

鉴于旧数据字典的种种缺点,MySQL-8.0对数据字典进行了较大的改动:把所有的元数据信息都存储在InnoDB dictionary table中,并且存储在单独的表空间mysql.ibd里,其架构如图3所示。下面逐一介绍各项改变的细节。


attachments-2019-12-woWN9IeP5df2350a10a1d.jpg


3.1 存储结构

MySQL下的原有系统表由MyISAM转为了InnoDB表,没有了proc和event表,直接改存到了dictionary table中。在debug模式下,可用如下指令查看dictionary tables:

3.2 Dictionary Object Cache

数据字典表信息可以通过全局的cache进行缓存。

table_definition_cache:存储表定义

schema_definition_cache:存储schema定义

stored_program_definition_cache:存储proc和func定义

tablespace_definition_cache:存储tablespace定义

另外还有character,collation,event,column_statistics也有cache,不过其大小硬编码不可配置:

3.3 Information_schema

attachments-2019-12-Tyn9SyTI5df2354303584.jpg


information_schema的变化如图4所示,主要包括以下几个方面:

1. information_schema部分表名变化

2. 通过information_schema查询时不再需要生成临时表获取,而是直接从数据字典表获取

3. 不需要像以前一样扫描文件夹获取数据库列表,不需要打开frm文件获取表信息,而是直接从数据字典表获取

4. information_schema查询以view的形式展现,更利于优化器优化查询

3.4 优点

去掉server层的元数据文件,元数据统一存储到InnoDB数据字典表,易于管理且crash-safe

支持原子DDL

information_schema查询更高效

4. Serialized Dictionary Information (SDI)

MySQL8.0不仅将元数据信息存储在数据字典表中,同时也冗余存储了一份在SDI中。对于非InnoDB表,SDI数据在后缀为.sdi的文件中,而对于innodb,SDI数据则直接存储与ibd中,如以下例子所示:

4.1 非事务表

上述例子中MyISAM表t2的SDI为test/t2_337.sdi,其中337为table_id, t2_337.sdi可以直接打开,数据是json格式(cat test/t2_337.sdi):

4.2 InnoDB事务表

上述例子中的InnoDB表t1的SDI则可以通过工具ibd2sdi可以解析出来(ibd2sdi test/t1.ibd):

SDI在ibd中实际是以表(BTree)的形式存储的。建表时会通过btr_sdi_create_index建立SDI的BTree,同时会向BTree插入table和tablespace的SDI信息,表的结构如下:

4.3 其他表空间的SDI

ibd2sdi mysql.ibd,可以查看所以mysql下的表,包括new dictionary和mysql下的普通表。需要注意的是ibdata1中不存放SDI信息,使用ibd2sdi解析它会出现以下提示:

[INFO] ibd2sdi: SDI is empty.

4.4 import

import (import table *.sdi)只支持MyISAM表,InnoDB不支持。由于SDI不包含trigger信息,所以import也不会导入trigger信息,trigger需额外处理。

5. Data Dictionary存取实现

例如create table 会涉及到mysql.tablespaces,mysql.tablespace_files, mysql.tables, mysql.indexes, mysql.columns,mysql.index_column_usage等。create table的过程如图5所示:


attachments-2019-12-pQJ9qJTb5df235961f7e8.jpg


下面以表t1为例,演示create table在DD中的数据分布:

drop table是create table的逆过程,不再具体分析。

6. Initialize

attachments-2019-12-jqgwL9n75df235c05e5ba.jpg

mysqld --initialize的源码流程如图6所示。具体过程为:

7. Atomic DDL

7.1 Atomic DDL

定义:DDL所涉及的以下更改操作是原子的,这些更改操作要么都提交,要么都回滚。

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.

7.2 DDL log

DDL过程中操作DD事物表是原子的,而DDL过程中也会操作文件,创建和释放BTree以及修改DD cache,这些操作不是原子的。为了实现atomic DDL, DDL过程中对文件操作和Btree操作等记录日志,这些日志会记录到DD表mysql.innodb_ddl_log中。日志有以下几个类型:

mysql.innodb_ddl_log 表结构如下:

将DDL分为以下几个阶段, Prepare记录DDL log,Post-DDL会replay log来提交或回滚DDL操作,同时也并清理DDL log。

7.3 Atomic DDL Examples

7.3.1 drop table

以drop table为例,drop 过程中会删除ibd文件,也会从mysql.innodb_dynamic_metadata 中删除相应记录。

在preppare阶段只是记录日志,没有真正删除。如果drop过程成功, innobase_post_ddl才从mysql.innodb_ddl_log中读取记录去replay,replay_delete_space_log/replay_drop_log会真正执行删除, replay完也会清理ddl log;如果drop过程失败,rollback时mysql.innodb_ddl_log的记录也回滚了,innobase_post_ddl时没有记录需要replay。

7.3.2 create table

drop table post_ddl阶段执行的redo操作,而create table post ddl执行的是rollback操作。create table prepare阶段会真正的创建ibd,BTree,修改DD share cache, 同时记录相应的log到mysql.innodb_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的回滚。

如果create table过程中发生crash, 重启后会读取ddl log完成ddl的回滚。

7.3.3 truncate table

truncate 先rename 为临时ibd,然后drop临时ibd,再重建表。rename会记录ddl log, 参考write_rename_space_log函数,删除重建也会记录ddl log, 同前面介绍的create/drop table, 可以做到原子。rollback时通过日志将临时ibd重命名为原ibd,参考replay_rename_space_log函数。

7.4 Atomic DDL带来的变化

drop 多表或多用户时,如果个别失败,整个DDL都会回滚,且不会记录binlog;而在MySQL8.0以前, 部分DDL会成功且整个DDL会记录binlog。

8. Persistent Autoinc

MySQL8.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中。

dict_table从dictionary cache淘汰时也会将autoinc_persisted持久化到mysql.innodb_dynamic_metadata中。

crash重启时,先从mysql.innodb_dynamic_metadata获取持久化的自增值,再从redo日志中读取最新的自增值, 参考MetadataRecover::parseMetadataLog,并通过MetadataRecover::apply更新到table->autoinc。

crash重启时,先从mysql.innodb_dynamic_metadata获取持久化的自增值,再从redo日志中读取最新的自增值, 参考MetadataRecover::parseMetadataLog,并通过MetadataRecover::apply更新到table->autoinc。

9. Upgrade

MySQL-8.0不支持跨版本升级,只能从5.7升级到8.0,不支持5.5,5.6直接升级到8.0。升级需要注意的问题

原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

10. 参考信息

https://github.com/mysql/mysql-server

https://dev.mysql.com/doc/refman/8.0/en/data-dictionary.html

https://dev.mysql.com/doc/refman/8.0/en/system-schema.html

https://mysqlserverteam.com/upgrading-to-mysql-8-0-here-is-what-you-need-to-know/

http://mysqlserverteam.com/mysql-8-0-improvements-to-information_schema/

https://dev.mysql.com/doc/refman/8.0/en/atomic-ddl.html

https://mysqlserverteam.com/bootstrapping-the-transactional-data-dictionary/

https://www.slideshare.net/StleDeraas/dd-and-atomic-ddl-pl17-dublin

  • 发表于 2019-12-12 20:35
  • 阅读 ( 78 )

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
shitian
shitian

662 篇文章

作家榜 »

  1. shitian 662 文章
  2. 石天 437 文章
  3. 每天惠23 33 文章
  4. 小A 29 文章