众所周知,由于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大版本的第一个开发版本,不排除未来行为会发生变化。
首先我们创建一个新库,并在库下创建两个表来开启我们的测试
mysql> CREATE DATABASE sbtest; Query OK, 1 row affected (0.00 sec) mysql> USE sbtest Database changed mysql> CREATE TABLE t1 (a int primary key); Query OK, 0 rows affected (0.00 sec) mysql> CREATE TABLE t2 (a int primary key, b int); Query OK, 0 rows affected (0.00 sec) $ls -lh /u01/my80/data/sbtest total 256K -rw-r----- 1 yinfeng.zwx users 128K Oct 5 19:44 t1.ibd -rw-r----- 1 yinfeng.zwx users 128K Oct 5 19:44 t2.ibd $ls /u01/my80/data/sbtest_9.SDI /u01/my80/data/sbtest_9.SDI $cat /u01/my80/data/sbtest_9.SDI { "sdi_version": 1, "dd_version": 1, "dd_object_type": "Schema", "dd_object": { "name": "sbtest", "default_collation_id": 33, "created": 0, "last_altered": 0 } }可以看到在库目录下只有ibd文件,并没有frm文件,而在数据目录下,相应的生成了一个SDI文件,来描述这个sbtest库的信息。
我们再来看看创建一个MYISAM引擎的表:
mysql> create database my; Query OK, 1 row affected (0.00 sec) mysql> use my Database changed mysql> create table t1 (a int, b varchar(320)) engine=myisam; Query OK, 0 rows affected (0.00 sec) $ls my/ t1_435.SDI t1.MYD t1.MYI { "sdi_version": 1, "dd_version": 1, "dd_object_type": "Table", "dd_object": { "name": "t1", "mysql_version_id": 80000, "created": 20161005201935, "last_altered": 20161005201935, "options": "avg_row_length=0;key_block_size=0;keys_disabled=0;pack_record=1;stats_auto_recalc=0;stats_sample_pages=0;", "columns": [ { "name": "a", "type": 4, "is_nullable": true, "is_zerofill": false, "is_unsigned": false, "is_auto_increment": false, "is_virtual": false, "hidden": false, "ordinal_position": 1, "char_length": 11, "numeric_precision": 10, "numeric_scale": 0, "datetime_precision": 0, "has_no_default": false, "default_value_null": true, "default_value": "", "default_option": "", "update_option": "", "comment": "", "generation_expression": "", "generation_expression_utf8": "", "options": "interval_count=0;", "se_private_data": "", "column_key": 1, "column_type_utf8": "int(11)", "elements": [], "collation_id": 33 }, { "name": "b", "type": 16, "is_nullable": true, "is_zerofill": false, "is_unsigned": false, "is_auto_increment": false, "is_virtual": false, "hidden": false, "ordinal_position": 2, "char_length": 960, "numeric_precision": 0, "numeric_scale": 0, "datetime_precision": 0, "has_no_default": false, "default_value_null": true, "default_value": "", "default_option": "", "update_option": "", "comment": "", "generation_expression": "", "generation_expression_utf8": "", "options": "interval_count=0;", "se_private_data": "", "column_key": 1, "column_type_utf8": "varchar(320)", "elements": [], "collation_id": 33 } ], "schema_ref": "my", "hidden": false, "se_private_id": 18446744073709551615, "engine": "MyISAM", "comment": "", "se_private_data": "", "row_format": 2, "partition_type": 0, "partition_expression": "", "default_partitioning": 0, "subpartition_type": 0, "subpartition_expression": "", "default_subpartitioning": 0, "indexes": [], "foreign_keys": [], "partitions": [], "collation_id": 33 } }这里我们创建了一个MyISAM表t1,相应的一个SDI文件被创建,文件中以JSON的格式记录了该表的详细信息。根据官方文件的描述,这个文件的存在是为了一个还未完全实现的功能。
一些新IS表使用View进行了重新设计,主要包括这些表:
CHARACTER_SETS COLLATIONS COLLATION_CHARACTER_SET_APPLICABILITY COLUMNS KEY_COLUMN_USAGE SCHEMATA STATISTICS TABLES TABLE_CONSTRAINTS VIEWS #例如SCHEMATA mysql> show create table information_schema.schemata\G *************************** 1. row *************************** View: SCHEMATA Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `information_schema`.`SCHEMATA` AS select `cat`.`name` AS `CATALOG_NAME`,`sch`.`name` AS `SCHEMA_NAME`,`cs`.`name` AS `DEFAULT_CHARACTER_SET_NAME`,`col`.`name` AS `DEFAULT_COLLATION_NAME`,NULL AS `SQL_PATH` from (((`mysql`.`schemata` `sch` join `mysql`.`catalogs` `cat` on((`cat`.`id` = `sch`.`catalog_id`))) join `mysql`.`collations` `col` on((`sch`.`default_collation_id` = `col`.`id`))) join `mysql`.`character_sets` `cs` on((`col`.`character_set_id` = `cs`.`id`))) where can_access_database(`sch`.`name`) character_set_client: utf8 collation_connection: utf8_general_ci 1 row in set (0.01 sec)也就是说,虽然DD系统表被隐藏不可见了,但你依然可以通过视图获得大部分信息。这种方式实际上大大加快了IS表的查询速度,转换成物理表的查询后,将无需为每个IS表的查询创建临时表(临时表的操作包含了server层创建frm, 引擎层获取数据or需要锁保护的全局数据)。另外优化器也能为IS表的查询选择更好的执行计划(例如使用系统表上的索引进行查询)。
官方对此做了测试,结果显示对IS表的查询性能大幅度提升,官方博客传送门:MySQL 8.0: Improvements to Information_schemaMySQL 8.0: Scaling and Performance of INFORMATION_SCHEMA
新选项: information_schema_stats: CACHED | LATEST
目前表的元数据信息缓存在statistics及tables表中以加速对IS表的查询性能。你可以通过参数information_schema_stats来直接读取已经缓存到内存的数据(cached),还是从存储引擎中获取最新的数据(latest). 很显然后者要慢一点。 而从is库下,可以看到对应两种表:TABLES及TABLES_DYNAMIC, 以及STATISTICS及STATISTICS_DYNAMIC。当被设置为LATEST时,就会去从**_DYNAMIC表中去读取数据。 该选项也会影响到SHOW TABLES等语句的行为。数据词典的结构发生巨大的变化后,相应的对于内存数据词典Cache也做改动,
mysql> show variables like '