解析MySQL8.0新特性——事务性数据字典与原子DDL

前言

事务性数据字典与原子DDL,是MySQL 8.0推出的两个非常重要的新特性,之所以将这两个新特性放在一起,是因为两者密切相关,事务性数据字典是前提,原子DDL是一个重要应用场景。

MySQL 8.0之前的数据字典

MySQL 8.0之前的数据字典,主要由以下三部分组成:

(1)操作系统文件

db.opt:数据库元数据信息
frm:表元数据信息
par:表分区元数据信息
TRN/TRG:触发器元数据信息
ddl_log.log:DDL过程中产生的元数据信息

(2)mysql库下的非InnoDB系统表

mysql> select table_schema,table_name,table_type,engine from information_schema.tables where table_schema='mysql' and engine<>'InnoDB';
+--------------+------------------+------------+--------+
| table_schema | table_name    | table_type | engine |
+--------------+------------------+------------+--------+
| mysql    | columns_priv   | BASE TABLE | MyISAM |
| mysql    | db        | BASE TABLE | MyISAM |
| mysql    | event      | BASE TABLE | MyISAM |
| mysql    | func       | BASE TABLE | MyISAM |
| mysql    | general_log   | BASE TABLE | CSV  |
| mysql    | ndb_binlog_index | BASE TABLE | MyISAM |
| mysql    | proc       | BASE TABLE | MyISAM |
| mysql    | procs_priv    | BASE TABLE | MyISAM |
| mysql    | proxies_priv   | BASE TABLE | MyISAM |
| mysql    | slow_log     | BASE TABLE | CSV  |
| mysql    | tables_priv   | BASE TABLE | MyISAM |
| mysql    | user       | BASE TABLE | MyISAM |
+--------------+------------------+------------+--------+
12 rows in set (0.00 sec)

(3)mysql库下的InnoDB系统表

mysql> select table_schema,table_name,table_type,engine from information_schema.tables where table_schema='mysql' and engine='InnoDB';
+--------------+---------------------------+------------+--------+
| table_schema | table_name        | table_type | engine |
+--------------+---------------------------+------------+--------+
| mysql    | engine_cost        | BASE TABLE | InnoDB |
| mysql    | gtid_executed       | BASE TABLE | InnoDB |
| mysql    | help_category       | BASE TABLE | InnoDB |
| mysql    | help_keyword       | BASE TABLE | InnoDB |
| mysql    | help_relation       | BASE TABLE | InnoDB |
| mysql    | help_topic        | BASE TABLE | InnoDB |
| mysql    | innodb_index_stats    | BASE TABLE | InnoDB |
| mysql    | innodb_table_stats    | BASE TABLE | InnoDB |
| mysql    | plugin          | BASE TABLE | InnoDB |
| mysql    | server_cost        | BASE TABLE | InnoDB |
| mysql    | servers          | BASE TABLE | InnoDB |
| mysql    | slave_master_info     | BASE TABLE | InnoDB |
| mysql    | slave_relay_log_info   | BASE TABLE | InnoDB |
| mysql    | slave_worker_info     | BASE TABLE | InnoDB |
| mysql    | time_zone         | BASE TABLE | InnoDB |
| mysql    | time_zone_leap_second   | BASE TABLE | InnoDB |
| mysql    | time_zone_name      | BASE TABLE | InnoDB |
| mysql    | time_zone_transition   | BASE TABLE | InnoDB |
| mysql    | time_zone_transition_type | BASE TABLE | InnoDB |
+--------------+---------------------------+------------+--------+
19 rows in set (0.00 sec)

我们可以看到,数据字典被分布到多个地方,一方面不利于元数据统一管理,另一方面容易造成数据的不一致(由于操作系统文件、非InnoDB系统表均不支持事务,执行DDL操作无法保证ACID)。

MySQL 8.0的数据字典

为了解决上述问题,MySQL 8.0将数据字典统一改进为InnoDB存储引擎存储,具体分为两部分:

(1)数据字典表:存放最重要的元数据信息,位于mysql库下,存储在mysql共享表空间(mysql.ibd)

(2)其他系统表:存放辅助的元数据信息,位于mysql库下,存储在mysql共享表空间(mysql.ibd)

数据字典表

数据字典表是不可见,既不能通过select访问,也不会出现在show tables或information.schema.tables结果里;尝试访问会报以下错误:

mysql> select * from mysql.tables limit 10;
ERROR 3554 (HY000): Access to data dictionary table 'mysql.tables' is rejected.

不过,在debug模式下,是可以访问这些隐藏的数据字典表的;我们重新编译安装(过程略),并以debug模式启动进程,再次尝试访问,结果如下:

mysql> SET SESSION debug='+d,skip_dd_table_access_check';

mysql> SELECT name, schema_id, hidden, type FROM mysql.tables where schema_id=1 AND hidden='System';
+------------------------------+-----------+--------+------------+
| name             | schema_id | hidden | type    |
+------------------------------+-----------+--------+------------+
| catalogs           |     1 | System | BASE TABLE |
| character_sets        |     1 | System | BASE TABLE |
| check_constraints      |     1 | System | BASE TABLE |
| collations          |     1 | System | BASE TABLE |
| column_statistics      |     1 | System | BASE TABLE |
| column_type_elements     |     1 | System | BASE TABLE |
| columns           |     1 | System | BASE TABLE |
| dd_properties        |     1 | System | BASE TABLE |
| events            |     1 | System | BASE TABLE |
| foreign_key_column_usage   |     1 | System | BASE TABLE |
| foreign_keys         |     1 | System | BASE TABLE |
| index_column_usage      |     1 | System | BASE TABLE |
| index_partitions       |     1 | System | BASE TABLE |
| index_stats         |     1 | System | BASE TABLE |
| indexes           |     1 | System | BASE TABLE |
| innodb_ddl_log        |     1 | System | BASE TABLE |
| innodb_dynamic_metadata   |     1 | System | BASE TABLE |
| parameter_type_elements   |     1 | System | BASE TABLE |
| parameters          |     1 | System | BASE TABLE |
| resource_groups       |     1 | System | BASE TABLE |
| routines           |     1 | System | BASE TABLE |
| schemata           |     1 | System | BASE TABLE |
| st_spatial_reference_systems |     1 | System | BASE TABLE |
| table_partition_values    |     1 | System | BASE TABLE |
| table_partitions       |     1 | System | BASE TABLE |
| table_stats         |     1 | System | BASE TABLE |
| tables            |     1 | System | BASE TABLE |
| tablespace_files       |     1 | System | BASE TABLE |
| tablespaces         |     1 | System | BASE TABLE |
| triggers           |     1 | System | BASE TABLE |
| view_routine_usage      |     1 | System | BASE TABLE |
| view_table_usage       |     1 | System | BASE TABLE |
+------------------------------+-----------+--------+------------+
32 rows in set (0.01 sec)

其他系统表

其他系统表,可以通过show tables或information_schema.tables查看,均以改进为InnoDB存储引擎(general_log、slow_log例外,这两张表并未记录元数据信息,只是用于记录日志):

mysql> select table_schema,table_name,engine from information_schema.tables where table_schema='mysql';
+--------------+---------------------------+--------+
| TABLE_SCHEMA | TABLE_NAME        | ENGINE |
+--------------+---------------------------+--------+
| mysql    | columns_priv       | InnoDB |
| mysql    | component         | InnoDB |
| mysql    | db            | InnoDB |
| mysql    | default_roles       | InnoDB |
| mysql    | engine_cost        | InnoDB |
| mysql    | func           | InnoDB |
| mysql    | general_log        | CSV  |
| mysql    | global_grants       | InnoDB |
| mysql    | gtid_executed       | InnoDB |
| mysql    | help_category       | InnoDB |
| mysql    | help_keyword       | InnoDB |
| mysql    | help_relation       | InnoDB |
| mysql    | help_topic        | InnoDB |
| mysql    | innodb_index_stats    | InnoDB |
| mysql    | innodb_table_stats    | InnoDB |
| mysql    | password_history     | InnoDB |
| mysql    | plugin          | InnoDB |
| mysql    | procs_priv        | InnoDB |
| mysql    | proxies_priv       | InnoDB |
| mysql    | role_edges        | InnoDB |
| mysql    | server_cost        | InnoDB |
| mysql    | servers          | InnoDB |
| mysql    | slave_master_info     | InnoDB |
| mysql    | slave_relay_log_info   | InnoDB |
| mysql    | slave_worker_info     | InnoDB |
| mysql    | slow_log         | CSV  |
| mysql    | tables_priv        | InnoDB |
| mysql    | time_zone         | InnoDB |
| mysql    | time_zone_leap_second   | InnoDB |
| mysql    | time_zone_name      | InnoDB |
| mysql    | time_zone_transition   | InnoDB |
| mysql    | time_zone_transition_type | InnoDB |
| mysql    | user           | InnoDB |
+--------------+---------------------------+--------+
33 rows in set (0.00 sec)

数据字典视图

刚刚提到,数据字典表只能在debug模式下访问,那么在生产环境中,我们应该怎么去获取元数据信息呢?答案是通过information_schema库下的数据字典视图。和Oracle数据库的设计理念一样,将元数据信息存放在基表中(x$、$),然后通过视图(v$、dba_/all_/user_)的方式提供给用户查询;MySQL数据库也是如此,将元数据信息存放在mysql库的数据字典表中隐藏起来,然后提供information_schema库视图给用户查询:

mysql> select table_schema,table_name,table_type,engine from information_schema.tables where table_schema='information_schema';
+--------------------+---------------------------------------+-------------+--------+
| TABLE_SCHEMA    | TABLE_NAME              | TABLE_TYPE | ENGINE |
+--------------------+---------------------------------------+-------------+--------+
| information_schema | ADMINISTRABLE_ROLE_AUTHORIZATIONS   | SYSTEM VIEW | NULL  |
| information_schema | APPLICABLE_ROLES           | SYSTEM VIEW | NULL  |
| information_schema | CHARACTER_SETS            | SYSTEM VIEW | NULL  |
| information_schema | CHECK_CONSTRAINTS           | SYSTEM VIEW | NULL  |
| information_schema | COLLATION_CHARACTER_SET_APPLICABILITY | SYSTEM VIEW | NULL  |
| information_schema | COLLATIONS              | SYSTEM VIEW | NULL  |
| information_schema | COLUMN_PRIVILEGES           | SYSTEM VIEW | NULL  |
| information_schema | COLUMN_STATISTICS           | SYSTEM VIEW | NULL  |
| information_schema | COLUMNS                | SYSTEM VIEW | NULL  |
| information_schema | ENABLED_ROLES             | SYSTEM VIEW | NULL  |
| information_schema | ENGINES                | SYSTEM VIEW | NULL  |
| information_schema | EVENTS                | SYSTEM VIEW | NULL  |
| information_schema | FILES                 | SYSTEM VIEW | NULL  |
| information_schema | INNODB_BUFFER_PAGE          | SYSTEM VIEW | NULL  |
| information_schema | INNODB_BUFFER_PAGE_LRU        | SYSTEM VIEW | NULL  |
| information_schema | INNODB_BUFFER_POOL_STATS       | SYSTEM VIEW | NULL  |
| information_schema | INNODB_CACHED_INDEXES         | SYSTEM VIEW | NULL  |
| information_schema | INNODB_CMP              | SYSTEM VIEW | NULL  |
| information_schema | INNODB_CMP_PER_INDEX         | SYSTEM VIEW | NULL  |
| information_schema | INNODB_CMP_PER_INDEX_RESET      | SYSTEM VIEW | NULL  |
| information_schema | INNODB_CMP_RESET           | SYSTEM VIEW | NULL  |
| information_schema | INNODB_CMPMEM             | SYSTEM VIEW | NULL  |
| information_schema | INNODB_CMPMEM_RESET          | SYSTEM VIEW | NULL  |
| information_schema | INNODB_COLUMNS            | SYSTEM VIEW | NULL  |
| information_schema | INNODB_DATAFILES           | SYSTEM VIEW | NULL  |
| information_schema | INNODB_FIELDS             | SYSTEM VIEW | NULL  |
| information_schema | INNODB_FOREIGN            | SYSTEM VIEW | NULL  |
| information_schema | INNODB_FOREIGN_COLS          | SYSTEM VIEW | NULL  |
| information_schema | INNODB_FT_BEING_DELETED        | SYSTEM VIEW | NULL  |
| information_schema | INNODB_FT_CONFIG           | SYSTEM VIEW | NULL  |
| information_schema | INNODB_FT_DEFAULT_STOPWORD      | SYSTEM VIEW | NULL  |
| information_schema | INNODB_FT_DELETED           | SYSTEM VIEW | NULL  |
| information_schema | INNODB_FT_INDEX_CACHE         | SYSTEM VIEW | NULL  |
| information_schema | INNODB_FT_INDEX_TABLE         | SYSTEM VIEW | NULL  |
| information_schema | INNODB_INDEXES            | SYSTEM VIEW | NULL  |
| information_schema | INNODB_METRICS            | SYSTEM VIEW | NULL  |
| information_schema | INNODB_SESSION_TEMP_TABLESPACES    | SYSTEM VIEW | NULL  |
| information_schema | INNODB_TABLES             | SYSTEM VIEW | NULL  |
| information_schema | INNODB_TABLESPACES          | SYSTEM VIEW | NULL  |
| information_schema | INNODB_TABLESPACES_BRIEF       | SYSTEM VIEW | NULL  |
| information_schema | INNODB_TABLESTATS           | SYSTEM VIEW | NULL  |
| information_schema | INNODB_TEMP_TABLE_INFO        | SYSTEM VIEW | NULL  |
| information_schema | INNODB_TRX              | SYSTEM VIEW | NULL  |
| information_schema | INNODB_VIRTUAL            | SYSTEM VIEW | NULL  |
| information_schema | KEY_COLUMN_USAGE           | SYSTEM VIEW | NULL  |
| information_schema | KEYWORDS               | SYSTEM VIEW | NULL  |
| information_schema | OPTIMIZER_TRACE            | SYSTEM VIEW | NULL  |
| information_schema | PARAMETERS              | SYSTEM VIEW | NULL  |
| information_schema | PARTITIONS              | SYSTEM VIEW | NULL  |
| information_schema | PLUGINS                | SYSTEM VIEW | NULL  |
| information_schema | PROCESSLIST              | SYSTEM VIEW | NULL  |
| information_schema | PROFILING               | SYSTEM VIEW | NULL  |
| information_schema | REFERENTIAL_CONSTRAINTS        | SYSTEM VIEW | NULL  |
| information_schema | RESOURCE_GROUPS            | SYSTEM VIEW | NULL  |
| information_schema | ROLE_COLUMN_GRANTS          | SYSTEM VIEW | NULL  |
| information_schema | ROLE_ROUTINE_GRANTS          | SYSTEM VIEW | NULL  |
| information_schema | ROLE_TABLE_GRANTS           | SYSTEM VIEW | NULL  |
| information_schema | ROUTINES               | SYSTEM VIEW | NULL  |
| information_schema | SCHEMA_PRIVILEGES           | SYSTEM VIEW | NULL  |
| information_schema | SCHEMATA               | SYSTEM VIEW | NULL  |
| information_schema | ST_GEOMETRY_COLUMNS          | SYSTEM VIEW | NULL  |
| information_schema | ST_SPATIAL_REFERENCE_SYSTEMS     | SYSTEM VIEW | NULL  |
| information_schema | ST_UNITS_OF_MEASURE          | SYSTEM VIEW | NULL  |
| information_schema | STATISTICS              | SYSTEM VIEW | NULL  |
| information_schema | TABLE_CONSTRAINTS           | SYSTEM VIEW | NULL  |
| information_schema | TABLE_PRIVILEGES           | SYSTEM VIEW | NULL  |
| information_schema | TABLES                | SYSTEM VIEW | NULL  |
| information_schema | TABLESPACES              | SYSTEM VIEW | NULL  |
| information_schema | TRIGGERS               | SYSTEM VIEW | NULL  |
| information_schema | USER_PRIVILEGES            | SYSTEM VIEW | NULL  |
| information_schema | VIEW_ROUTINE_USAGE          | SYSTEM VIEW | NULL  |
| information_schema | VIEW_TABLE_USAGE           | SYSTEM VIEW | NULL  |
| information_schema | VIEWS                 | SYSTEM VIEW | NULL  |
+--------------------+---------------------------------------+-------------+--------+
73 rows in set (0.00 sec)

mysql> show create table information_schema.tables\G
*************************** 1. row ***************************
        View: TABLES
     Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`mysql.infoschema`@`localhost` SQL SECURITY DEFINER VIEW `information_schema`.`TABLES` AS select (`cat`.`name` collate utf8_tolower_ci) AS `TABLE_CATALOG`,(`sch`.`name` collate utf8_tolower_ci) AS `TABLE_SCHEMA`,(`tbl`.`name` collate utf8_tolower_ci) AS `TABLE_NAME`,`tbl`.`type` AS `TABLE_TYPE`,if((`tbl`.`type` = 'BASE TABLE'),`tbl`.`engine`,NULL) AS `ENGINE`,if((`tbl`.`type` = 'VIEW'),NULL,10) AS `VERSION`,`tbl`.`row_format` AS `ROW_FORMAT`,if((`tbl`.`type` = 'VIEW'),NULL,internal_table_rows(`sch`.`name`,`tbl`.`name`,if((`tbl`.`partition_type` is null),`tbl`.`engine`,''),`tbl`.`se_private_id`,(`tbl`.`hidden` <> 'Visible'),`ts`.`se_private_data`,coalesce(`stat`.`table_rows`,0),coalesce(cast(`stat`.`cached_time` as unsigned),0))) AS `TABLE_ROWS`,if((`tbl`.`type` = 'VIEW'),NULL,internal_avg_row_length(`sch`.`name`,`tbl`.`name`,if((`tbl`.`partition_type` is null),`tbl`.`engine`,''),`tbl`.`se_private_id`,(`tbl`.`hidden` <> 'Visible'),`ts`.`se_private_data`,coalesce(`stat`.`avg_row_length`,0),coalesce(cast(`stat`.`cached_time` as unsigned),0))) AS `AVG_ROW_LENGTH`,if((`tbl`.`type` = 'VIEW'),NULL,internal_data_length(`sch`.`name`,`tbl`.`name`,if((`tbl`.`partition_type` is null),`tbl`.`engine`,''),`tbl`.`se_private_id`,(`tbl`.`hidden` <> 'Visible'),`ts`.`se_private_data`,coalesce(`stat`.`data_length`,0),coalesce(cast(`stat`.`cached_time` as unsigned),0))) AS `DATA_LENGTH`,if((`tbl`.`type` = 'VIEW'),NULL,internal_max_data_length(`sch`.`name`,`tbl`.`name`,if((`tbl`.`partition_type` is null),`tbl`.`engine`,''),`tbl`.`se_private_id`,(`tbl`.`hidden` <> 'Visible'),`ts`.`se_private_data`,coalesce(`stat`.`max_data_length`,0),coalesce(cast(`stat`.`cached_time` as unsigned),0))) AS `MAX_DATA_LENGTH`,if((`tbl`.`type` = 'VIEW'),NULL,internal_index_length(`sch`.`name`,`tbl`.`name`,if((`tbl`.`partition_type` is null),`tbl`.`engine`,''),`tbl`.`se_private_id`,(`tbl`.`hidden` <> 'Visible'),`ts`.`se_private_data`,coalesce(`stat`.`index_length`,0),coalesce(cast(`stat`.`cached_time` as unsigned),0))) AS `INDEX_LENGTH`,if((`tbl`.`type` = 'VIEW'),NULL,internal_data_free(`sch`.`name`,`tbl`.`name`,if((`tbl`.`partition_type` is null),`tbl`.`engine`,''),`tbl`.`se_private_id`,(`tbl`.`hidden` <> 'Visible'),`ts`.`se_private_data`,coalesce(`stat`.`data_free`,0),coalesce(cast(`stat`.`cached_time` as unsigned),0))) AS `DATA_FREE`,if((`tbl`.`type` = 'VIEW'),NULL,internal_auto_increment(`sch`.`name`,`tbl`.`name`,if((`tbl`.`partition_type` is null),`tbl`.`engine`,''),`tbl`.`se_private_id`,(`tbl`.`hidden` <> 'Visible'),`ts`.`se_private_data`,coalesce(`stat`.`auto_increment`,0),coalesce(cast(`stat`.`cached_time` as unsigned),0),`tbl`.`se_private_data`)) AS `AUTO_INCREMENT`,`tbl`.`created` AS `CREATE_TIME`,if((`tbl`.`type` = 'VIEW'),NULL,internal_update_time(`sch`.`name`,`tbl`.`name`,if((`tbl`.`partition_type` is null),`tbl`.`engine`,''),`tbl`.`se_private_id`,(`tbl`.`hidden` <> 'Visible'),`ts`.`se_private_data`,coalesce(cast(`stat`.`update_time` as unsigned),0),coalesce(cast(`stat`.`cached_time` as unsigned),0))) AS `UPDATE_TIME`,if((`tbl`.`type` = 'VIEW'),NULL,internal_check_time(`sch`.`name`,`tbl`.`name`,if((`tbl`.`partition_type` is null),`tbl`.`engine`,''),`tbl`.`se_private_id`,(`tbl`.`hidden` <> 'Visible'),`ts`.`se_private_data`,coalesce(cast(`stat`.`check_time` as unsigned),0),coalesce(cast(`stat`.`cached_time` as unsigned),0))) AS `CHECK_TIME`,`col`.`name` AS `TABLE_COLLATION`,if((`tbl`.`type` = 'VIEW'),NULL,internal_checksum(`sch`.`name`,`tbl`.`name`,if((`tbl`.`partition_type` is null),`tbl`.`engine`,''),`tbl`.`se_private_id`,(`tbl`.`hidden` <> 'Visible'),`ts`.`se_private_data`,coalesce(`stat`.`checksum`,0),coalesce(cast(`stat`.`cached_time` as unsigned),0))) AS `CHECKSUM`,if((`tbl`.`type` = 'VIEW'),NULL,get_dd_create_options(`tbl`.`options`,if((ifnull(`tbl`.`partition_expression`,'NOT_PART_TBL') = 'NOT_PART_TBL'),0,1),if((`sch`.`default_encryption` = 'YES'),1,0))) AS `CREATE_OPTIONS`,internal_get_comment_or_error(`sch`.`name`,`tbl`.`name`,`tbl`.`type`,`tbl`.`options`,`tbl`.`comment`) AS `TABLE_COMMENT` from (((((`mysql`.`tables` `tbl` join `mysql`.`schemata` `sch` on((`tbl`.`schema_id` = `sch`.`id`))) join `mysql`.`catalogs` `cat` on((`cat`.`id` = `sch`.`catalog_id`))) left join `mysql`.`collations` `col` on((`tbl`.`collation_id` = `col`.`id`))) left join `mysql`.`tablespaces` `ts` on((`tbl`.`tablespace_id` = `ts`.`id`))) left join `mysql`.`table_stats` `stat` on(((`tbl`.`name` = `stat`.`table_name`) and (`sch`.`name` = `stat`.`schema_name`)))) where ((0 <> can_access_table(`sch`.`name`,`tbl`.`name`)) and (0 <> is_visible_dd_object(`tbl`.`hidden`)))
character_set_client: utf8
collation_connection: utf8_general_ci
1 row in set (0.00 sec)

数据字典缓存

为了减少磁盘IO,提高访问效率,MySQL 8.0引入了数据字典缓存。数据字典缓存是一块全局共享区域,通过LRU算法进行内存管理,具体包括:

tablespace definition cache partition:用于缓存表空间定义对象;大小限制由参数tablespace_definition_cache决定。
schema definition cache partition:用于缓存模式定义对象;大小限制由参数schema_definition_cache决定。
table definition cache partition:用于缓存表定义对象;大小限制由参数max_connections决定。
stored program definition cache partition:用于缓存存储过程定义对象;大小限制由参数stored_program_definition_cache决定。
character set definition cache partition:用于缓存字符集定义对象;硬编码限制256个。
collation definition cache partition:用于缓存排序规则定义对象;硬编码限制256个。

原子DDL

首先,了解一下什么是原子性?原子性是指,一个事务执行要么全部成功,要么全部失败。

在MySQL 8.0之前,由于不支持原子DDL,在服务进程异常挂掉或服务器异常宕机的情况下,有可能会导致数据字典、存储引擎结构、二进制日志之间的不一致。

在MySQL 8.0中,数据字典均被改造成InnoDB存储引擎表,原子DDL也被引入进来。原子DDL是将数据字典更新、存储引擎操作、二进制日志写入放到同一个事务里执行,要么全部成功提交,要么全部失败回滚。

接下来,我们还是先通过一个例子,来了解一下原子DDL。在这个例子中,DROP TABLE t1, t2属于同一个事务;在5.7版本中,出现了一个事务部分、成功部分失败的情况,即DROP TABLE t1成功、DROP TABLE t2失败;但在8.0版本中,因为DROP TABLE t2失败,导致整个事务全部失败回滚;这个例子就很好地体现了原子性和非原子性的区别。

5.7版本:
mysql> CREATE TABLE t1 (c1 INT);
mysql> DROP TABLE t1, t2;
ERROR 1051 (42S02): Unknown table 'test.t2'
mysql> SHOW TABLES;
Empty set (0.00 sec)

8.0版本:
mysql> CREATE TABLE t1 (c1 INT);
mysql> DROP TABLE t1, t2;
ERROR 1051 (42S02): Unknown table 'test.t2'
mysql> SHOW TABLES;
+----------------+
| Tables_in_test |
+----------------+
| t1       |
+----------------+

在对原子DDL有初步了解后,接下来介绍一下具体过程:

(1)prepare:创建需要的对象,并将ddl日志写入到mysql.innodb_ddl_log;ddl日志记录了如何前滚和回滚ddl操作。
(2)perform:执行ddl操作。
(3)commit:更新数据字典并提交。
(4)post-ddl:重放和删除ddl日志。只有在实例异常宕机情况下,ddl日志才会继续保存在mysql.innodb_ddl_log;在在实例重启后,进行实例恢复阶段,ddl日志会重放和删除;如果第3步-数据字典更新已经成功提交,并写入redo log和binlog,那么ddl操作成功;否则,ddl操作失败,并根据ddl日志进行回滚

最后,再介绍一下,怎么查看DDL日志?

其中一个方法,是在debug级别下,访问表mysql.innodb_ddl_log进行查看(不推荐)

CREATE TABLE mysql.innodb_ddl_log (
 id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
 thread_id BIGINT UNSIGNED NOT NULL,
 type INT UNSIGNED NOT NULL,
 space_id INT UNSIGNED,
 page_no INT UNSIGNED,
 index_id BIGINT UNSIGNED,
 table_id BIGINT UNSIGNED,
 old_file_path VARCHAR(512) COLLATE UTF8_BIN,
 new_file_path VARCHAR(512) COLLATE UTF8_BIN,
 KEY(thread_id)
);

另一个办法,是可以将DDL日志打印到error log进行查看(推荐)

mysql> set global innodb_print_ddl_logs=on;
Query OK, 0 rows affected (0.00 sec)

mysql> set global LOG_ERROR_VERBOSITY=3;
Query OK, 0 rows affected (0.00 sec)

mysql> create table test(id int);
Query OK, 0 rows affected (0.04 sec)

$ tail -100f mysql-error.log
2020-08-17T19:55:09.804345+08:00 73 [Note] [MY-012473] [InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=57, thread_id=73, space_id=12, old_file_path=./test/test.ibd]
2020-08-17T19:55:09.804396+08:00 73 [Note] [MY-012478] [InnoDB] DDL log delete : 57
2020-08-17T19:55:09.816850+08:00 73 [Note] [MY-012477] [InnoDB] DDL log insert : [DDL record: REMOVE CACHE, id=58, thread_id=73, table_id=1069, new_file_path=test/test]
2020-08-17T19:55:09.816887+08:00 73 [Note] [MY-012478] [InnoDB] DDL log delete : 58
2020-08-17T19:55:09.820623+08:00 73 [Note] [MY-012472] [InnoDB] DDL log insert : [DDL record: FREE, id=59, thread_id=73, space_id=12, index_id=160, page_no=4]
2020-08-17T19:55:09.820673+08:00 73 [Note] [MY-012478] [InnoDB] DDL log delete : 59
2020-08-17T19:55:09.837695+08:00 73 [Note] [MY-012485] [InnoDB] DDL log post ddl : begin for thread id : 73
2020-08-17T19:55:09.837721+08:00 73 [Note] [MY-012486] [InnoDB] DDL log post ddl : end for thread id : 73

总结

MySQL 8.0对于数据字典的改进,带来了很多好处,包括元数据统一管理、数据字典缓存、information_schema性能提升、原子DDL等等。

以上就是解析MySQL8.0新特性——事务性数据字典与原子DDL的详细内容,更多关于MySQL8.0新特性的资料请关注我们其它相关文章!

(0)

相关推荐

  • MySQL8新特性:自增主键的持久化详解

    前言 自增主键没有持久化是个比较早的bug,这点从其在官方bug网站的id号也可看出(https://bugs.mysql.com/bug.php?id=199).由Peter Zaitsev(现Percona CEO)于2003年提出.历史悠久且臭名昭著. 首先,直观的重现下. mysql> create table t1(id int auto_increment primary key); Query OK, 0 rows affected (0.01 sec) mysql> inser

  • MySQL 8 新特性之Invisible Indexes

    背景 索引是把双刃剑,在提升查询速度的同时会减慢DML的操作.毕竟,索引的维护需要一定的成本.所以,对于索引,要加上该加的,删除无用的.前者是加法,后者是减法.但在实际工作中,大家似乎更热衷于前者,而很少进行后者.究其原因,在于后者,难.难的不是操作本身,而是如何确认一个索引是无用的. 如何确认无用索引 在不可见索引出现之前,大家可以通过sys.schema_unused_indexes来确定无用索引.在MySQL 5.6中,即使没有sys库,也可通过该视图的基表来进行查询. mysql> sh

  • MySQL8新特性:持久化全局变量的修改方法

    前言 在8之前的版本中,对于全局变量的修改,其只会影响其内存值,而不会持久化到配置文件中.数据库重启,又会恢复成修改前的值.从8开始,可通过SET PERSIST命令将全局变量的修改持久化到配置文件中. 试举一例 mysql> show variables like '%max_connections%'; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+

  • MySQL 8.0 新特性之哈希连接(Hash Join)

    MySQL 开发组于 2019 年 10 月 14 日 正式发布了 MySQL 8.0.18 GA 版本,带来了一些新特性和增强功能.其中最引人注目的莫过于多表连接查询支持 hash join 方式了.我们先来看看官方的描述: MySQL 实现了用于内连接查询的 hash join 方式.例如,从 MySQL 8.0.18 开始以下查询可以使用 hash join 进行连接查询: SELECT * FROM t1 JOIN t2 ON t1.c1=t2.c1; Hash join 不需要索引的支

  • MySQL8.0新特性之支持原子DDL语句

    MySQL 8.0开始支持原子数据定义语言(DDL)语句.此功能称为原子DDL.原子DDL语句将与DDL操作关联的数据字典更新,存储引擎操作和二进制日志写入组合到单个原子事务中.即使服务器在操作期间暂停,也会提交事务,并将适用的更改保留到数据字典,存储引擎和二进制日志,或者回滚事务. 通过在MySQL 8.0中引入MySQL数据字典,可以实现Atomic DDL.在早期的MySQL版本中,元数据存储在元数据文件,非事务性表和存储引擎特定的字典中,这需要中间提交.MySQL数据字典提供的集中式事务

  • MySQL8新特性:降序索引详解

    前言 MySQL 8.0终于支持降序索引了.其实,从语法上,MySQL 4就支持了,但正如官方文档所言,"they are parsed but ignored",实际创建的还是升序索引. 无图无真相,同一个建表语句,看看MySQL 5.7和8.0的区别. create table slowtech.t1(c1 int,c2 int,index idx_c1_c2(c1,c2 desc)); MySQL 5.7 mysql> show create table slowtech.

  • 浅谈mysql8.0新特性的坑和解决办法(小结)

    一.创建用户和授权 在mysql8.0创建用户和授权和之前不太一样了,其实严格上来讲,也不能说是不一样,只能说是更严格,mysql8.0需要先创建用户和设置密码,然后才能授权. #先创建一个用户 create user 'hong'@'%' identified by '123123'; #再进行授权 grant all privileges on *.* to 'hong'@'%' with grant option; 如果还是用原来5.7的那种方式,会报错误: grant all privi

  • MySQL8新特性之降序索引底层实现详解

    什么是降序索引 大家可能对索引比较熟悉,而对降序索引比较陌生,事实上降序索引是索引的子集. 我们通常使用下面的语句来创建一个索引: create index idx_t1_bcd on t1(b,c,d); 上面sql的意思是在t1表中,针对b,c,d三个字段创建一个联合索引. 但是大家不知道的是,上面这个sql实际上和下面的这个sql是等价的: create index idx_t1_bcd on t1(b asc,c asc,d asc); asc表示的是升序,使用这种语法创建出来的索引叫做

  • 解析MySQL8.0新特性——事务性数据字典与原子DDL

    前言 事务性数据字典与原子DDL,是MySQL 8.0推出的两个非常重要的新特性,之所以将这两个新特性放在一起,是因为两者密切相关,事务性数据字典是前提,原子DDL是一个重要应用场景. MySQL 8.0之前的数据字典 MySQL 8.0之前的数据字典,主要由以下三部分组成: (1)操作系统文件 db.opt:数据库元数据信息 frm:表元数据信息 par:表分区元数据信息 TRN/TRG:触发器元数据信息 ddl_log.log:DDL过程中产生的元数据信息 (2)mysql库下的非InnoD

  • MySQL 8.0 新特性之检查约束的实现

    大家好,我是只谈技术不剪发的 Tony 老师.这次我们来介绍一个 MySQL 8.0 增加的新功能:检查约束(CHECK ). SQL 中的检查约束属于完整性约束的一种,可以用于约束表中的某个字段或者一些字段必须满足某个条件.例如用户名必须大写.余额不能小于零等. 我们常见的数据库都实现了检查约束,例如 Oracle.SQL Server.PostgreSQL 以及 SQLite:然而 MySQL 一直以来没有真正实现该功能,直到最新的 MySQL 8.0.16. MySQL 8.0.15 之前

  • AngularJS 2.0新特性有哪些

    AngularJS已然成为Web应用开发世界里最受欢迎的开源JavaScript框架.自成立以来,见证其成功的是惊人的经济增长以及团体的支持与采用--包括个人开发者.企业.社区. Angular已经变成一个构建复杂单页面应用的客户端MVW框架(Model-View-Whatever).它在应用测试和应用编写方面都扮演重要角色,同时简化了开发过程. Angular目前的版本为1.3,该版本稳定,并被谷歌(框架维护者)用于支持众多应用(据估计,在谷歌有超过1600个应用运行于Angular1.2或1

  • Angular5.0.0新特性

    文章来自官网部分翻译 https://blog.angular.io/version-5-0-0-of-angular-now-available-37e414935ced Angular5.0.0版本已经正式发布 总结一下v5.0.0带来的新变化都有哪些. 1.构建优化 5.0版本默认采用CLI构建和打包.构建优化器是包含在CLI里面的一个工具,通过对你的应用程序更加语义化的理解可以使得你的打包程序(bundle)更小. 构建优化器有两个主要工作. 第一,我们可以将应用程序的一部分标记为纯应用

  • Android Studio 4.0新特性及升级异常问题的解决方案

    一.升级问题 1. dataBinding开启配置修改 升级到AS 4.0以后,出现如下的预警,对于我这种有代码洁癖的人是不能忍的,必须解决 DSL element 'android.dataBinding.enabled' is obsolete and has been replaced with 'android.buildFeatures.dataBinding' 解决方法: dataBinding { enabled = true } 这是原有的DataBinding开启方式,在升级后

  • 详解MySQL8的新特性ROLE

    [MySQL的ROLE解决了什么问题] 假设你是一个职业素养良好的DBA比较同时又比较注重权限管理的话:可能遇到过这样的问题,数据库中有多个开发人员的账号:有一天要建 一个新的schema,如果你希望之前所有的账号都能操作这个schema下的表的话,在mysql-8.0之前你要对第一个账号都单独的赋一次权. mysql-8.0.x所权限抽象了出来用ROLE来表示,当你为ROLE增加新的权限的时候,与这个ROLE关联的所有用户的权限也就一并变化了:针对 上面提到的场景在mysql-8.0.x下只要

  • MySQL 8.0新特性之隐藏字段的深入讲解

    前言 MySQL 8.0.23 版本增加了一个新的功能:隐藏字段(Invisible Column),也称为不可见字段.本文给大家介绍一下 MySQL 隐藏字段的相关概念和具体实现. 基本概念 隐藏字段需要在查询中进行显式引用,否则对查询而言是不可见的.MySQL 8.0.23 开始支持隐藏字段,在此之前所有的字段都是可见字段. 考虑以下应用场景,假如一个应用程序使用SELECT *语句访问某个表,并且必需持续不断地进行查询,即使我们为该表增加了一个该应用不需要的新字段时也要求能够正常工作.对于

  • C# 8.0新特性介绍

    C# 语言是在2000发布的,至今已正式发布了7个版本,每个版本都包含了许多令人兴奋的新特性和功能更新.同时,C# 每个版本的发布都与同时期的 Visual Studio 以及 .NET 运行时版本高度耦合,这也有助于开发者更好的学习掌握 C#,并将其与 Visual Studio 以及 .NET 的使用结合起来. 加快 C# 版本的发布速度 在被称为"新微软"的推动下,微软创新的步伐也加快了.为了做到加快步伐,微软开发部门将一些过去集成在一起的技术现在都分离了出来. Visual S

随机推荐