码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 解读MySQL 8.0数据字典的初始化与启动


    MySQL 8.0新设计实现的数据字典,解决了之前版本的数据字典冗余,DDL原子性、crash safe等问题。
    合集 - 数据库最新分享(62)
    1.华为云峰会2024,GaussDB扬帆出海,给世界一个更优选择02-282.预算有限,资源冗余?DWS集群缩容如何帮你解决烦劳02-293.数智融合,华为云GaussDB(for MySQL)助力企业释放数据新价值03-014.RDS for MySQL Serverless公测上线:弹性伸缩,最高可降成本超80%03-045.GaussDB跨云容灾:实现跨地域的数据库高可用能力03-056.实例详解如何构建动态SQL语句03-057.GaussDB(DWS)运维利刃:TopSQL工具解析03-078.手把手带你认识GaussDB轻量化运维管理工具03-089.守护更多女性健康,华为云GeminiDB助力美柚数据库高效稳定迁移03-0810.GaussDB(DWS)集群通信:详解pooler连接池03-1111.Libcomm通信库:GaussDB(DWS) 为解决建联过多的小妙招03-1212.实例带你了解GaussDB数据库的LOCK TABLE03-1213.RDS for MariaDB“智能DBA助手”,让运维效率嗖嗖地!03-1214.实例带你了解GaussDB的索引管理03-1415.详解GaussDB(DWS)中3个防过载检查项03-1816.华为云数据库创新发展论坛,打造行业更优数据库底座!03-1917.GaussDB(分布式)实例故障处理03-1918.华为云GeminiDB新版本发布:全面支持Redis 6.203-2019.究竟什么样的数据库,才能承接RTA广告这个技术活!03-2020.GaussDB(DWS) 业务高可靠的三大利器:CN RETRY、远程读、ELB03-2521.走在前、做示范,苏州农商银行携华为云完成超级网银系统改造03-2522.分布式数据库技术的演进和发展方向03-2623.新版Redis不再“开源”,对使用者都有哪些影响?03-2724.cgroup、资源池、用户的关系..涉及到GaussDB(DWS)的资源设置03-2925.GeminiDB Cassandra接口新特性FLASHBACK发布:任意时间点秒级闪回04-0126.探索GaussDB(DWS)湖仓融合:Hudi与元数据打通的深度解析04-0127.详解数仓对象设计中序列SEQUENCE原理与应用04-0228.数仓调优实战:GUC参数调优04-0729.详讲openGauss 5.0 单点企业版如何部署_Centos7_x8604-0830.华为云GeminiDB,广告RTA的“登云梯”04-0931.DTC2024,华为云数据库创新融合大发展,打造世界级数据库!04-1732.GaussDB(DWS)基于Flink的实时数仓构建04-1833.数仓的两种轻量级数据交换格式:json与jsonb04-1934.重磅新品发布!云耀数据库HRDS,享受轻量级的极致体验04-2335.“企业创新新引擎”数据库专项赋能会,让云原生技术普惠千行百业!04-2436.GaussDB SQL查询语句执行过程解析04-2437.详解数仓的向量化执行引擎04-2538.Redis开源社区持续壮大,华为云为Valkey项目注入新的活力05-0639.详解数仓的3A安全能力05-0740.【GaussDB(for MySQL)】 Big IN查询优化05-0941.GaussDB细粒度资源管控技术透视05-0942.带你了解GaussDB SQL中的BOOLEAN表达式05-1043.数仓安全:数据脱敏技术深度解析05-1044.详解GaussDB(DWS)中的行执行引擎05-1145.了解GaussDB SQL中CASE表达式05-1346.JDBC连接openGauss6.0和PostgreSQL16.2性能对比05-1447.MySQL 给用户添加 ALTER VIEW 的权限05-1548.MySQL全文索引源码剖析之Insert语句执行过程05-2049.全球厂商之最,华为17篇论文入选国际数据库顶会ICDE05-2250.GeminiDB PITR,让游戏回档“进退自如”!05-2451.浅析MySQL 8.0直方图原理05-2752.LLVM技术在GaussDB等数据库中的应用06-0353.告别内存OOM,解决MySQL内存增长问题06-0454.从数据库设计到性能调优,全面掌握openGemini应用开发最佳实践06-0455.深度体验与测评openGauss 6.0.0新版本06-1156.深度解读数据库引入LLVM技术后如何提升性能06-1257.从Purge机制说起,详解GaussDB(for MySQL)的优化策略06-1758.攀登不止,华为数据库论文入选SIGMOD 2024,技术创新再谱新篇06-1859.技术解读数据库如何实现“多租户”?06-20
    60.解读MySQL 8.0数据字典的初始化与启动06-24
    61.GeminiDB全面联动MySQL:热点数据,一键加速06-2662.探秘数据库中的并行计算技术应用07-01
    收起

    本文分享自华为云社区《MySQL全文索引源码剖析之Insert语句执行过程》,作者:GaussDB 数据库。

    本文主要介绍MySQL 8.0数据字典的基本概念和数据字典的初始化与启动加载的主要流程。

    MySQL 8.0数据字典简介

    数据字典(Data Dictionary, DD)用来存储数据库内部对象的信息,这些信息也被称为元数据(Metadata),包括schema名称、表结构、存储过程的定义等。

     

    1.PNG

    图1 MySQL 8.0之前的数据字典

    图片来源:MySQL 8.0 Data Dictionary: Background and Motivation

    如图1所示,MySQL 8.0之前的元数据,分散存储在许多不同的位置,包括各种元数据文件,不支持事务的表和存储引擎特有的数据字典等;Server层和存储引擎层有各自的数据字典,其中一部分是重复的。

    以上的设计导致支持原子的DDL变得很困难,因此MySQL 8.0之前,如果DDL过程中发生crash,后期的恢复很容易出现各种问题,导致表无法访问、复制异常等。

    如图2所示,MySQL 8.0使用支持事务的InnoDB存储引擎作来存储元数据,实现数据字典的统一管理。这个改进消除了元数据存储的冗余,通过支持原子DDL,实现了DDL的crash safe。

    2.PNG

    图2 MySQL 8.0数据字典

    图片来源:MySQL 8.0: Data Dictionary Architecture and Design

    数据字典表都是隐藏的,只有在debug编译模式下,可以通过设置开关set debug='+d,skip_dd_table_access_check'来直接查看数据字典表。

    复制代码
    mysql> set debug='+d,skip_dd_table_access_check';
    Query OK, 0 rows affected (0.01 sec)
    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)
    复制代码

    上面查询得到的表就是隐藏的数据字典表,MySQL的元数据存储在这些表中。

    在release编译模式下,如果要查看数据字典信息,只能通过INFORMATION_SCHEMA中的视图来查询。例如,可以通过视图information_schema.tables查询数据字典表mysql.tables。

    复制代码
    mysql> select TABLE_SCHEMA,TABLE_NAME,TABLE_TYPE,ENGINE
        -> from information_schema.tables
        -> where TABLE_SCHEMA = 'sbtest' limit 1;
    +--------------+------------+------------+--------+
    | TABLE_SCHEMA | TABLE_NAME | TABLE_TYPE | ENGINE |
    +--------------+------------+------------+--------+
    | sbtest       | sbtest1    | BASE TABLE | InnoDB |
    +--------------+------------+------------+--------+
    1 row in set (0.00 sec)
    复制代码

    数据字典表的相关代码

    数据字典的代码位于sql/dd目录,所有数据字典相关的信息都在dd这个命名空间中,各数据字典表本身的定义位于sql/dd/impl/tables目录的代码中,可以理解为数据字典表的元数据在代码中已经定义好了。

    以存储schema信息的schemata表为例,其类的声明如下:

    复制代码
    class Schemata : public Entity_object_table_impl {
    public:
      // ...  
     // 所包含的字段  enum enum_fields {
        FIELD_ID,
        FIELD_CATALOG_ID,
        FIELD_NAME,
        FIELD_DEFAULT_COLLATION_ID,
        FIELD_CREATED,
        FIELD_LAST_ALTERED,
        FIELD_OPTIONS,
        FIELD_DEFAULT_ENCRYPTION,
        FIELD_SE_PRIVATE_DATA,
        NUMBER_OF_FIELDS  // Always keep this entry at the end of the enum  };
      // 所包含的索引
      enum enum_indexes {
        INDEX_PK_ID = static_cast<uint>(Common_index::PK_ID),
        INDEX_UK_CATALOG_ID_NAME = static_cast<uint>(Common_index::UK_NAME),
        INDEX_K_DEFAULT_COLLATION_ID
      };
      // 所包含的外键
      enum enum_foreign_keys { FK_CATALOG_ID, FK_DEFAULT_COLLATION_ID };
      // ...
    };
    复制代码

    其构造函数定义了该表的名称、各字段、索引和外键等信息,以及该表默认存储的数据信息,如下所示:

    复制代码
    Schemata::Schemata() {
      // 表名  m_target_def.set_table_name("schemata");
      // 字段定义
      m_target_def.add_field(FIELD_ID, "FIELD_ID",
                             "id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT");
      // ...
      // 索引定义
      m_target_def.add_index(INDEX_PK_ID, "INDEX_PK_ID", "PRIMARY KEY (id)");
      // ...
      // 外键定义
      m_target_def.add_foreign_key(FK_CATALOG_ID, "FK_CATALOG_ID",
                                   "FOREIGN KEY (catalog_id) REFERENCES \ 
                                   catalogs(id)");
      // ...
      // 初始化时额外需要执行的DML语句
      m_target_def.add_populate_statement(
          "INSERT INTO schemata (catalog_id, name, default_collation_id, created, "
          "last_altered, options, default_encryption, se_private_data) VALUES "
          "(1,'information_schema',33, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, "
          "NULL, 'NO', NULL)");
    }
    复制代码

    在初始化和启动时,会使用Object_table_definition_impl::get_ddl()函数来获取m_target_def中信息所生成的DDL语句,创建出schemata表;使用Object_table_definition_impl::get_dml()获取DML语句,用于初始化表中的数据。

    dd::tables::Schemata类的继承关系,如图3。所有的数据字典表对应的类,最终都是派生自dd::Object_table,便于统一处理。

    3.PNG

    图3 dd::tables::Schemata类

    对于这些表中存储的元数据所对应的对象,或者说这些表中的每一行数据所对应的一个对象,比如一个schema、table、column等,代码中也有对应的类。

    还是以schema为例,它对应的类是dd::Schema,实现类是dd::Schema_impl,代表的是schema这种数据库内部对象,也是mysql.schemata表中的一行。

    所有数据字典中所存储的对象在代码中的基类都是dd::Weak_object,如图4:

    4.PNG

    图4 dd::Schema_impl类

    schema的id和name在dd::Entity_object_impl中,其他字段在实现类dd::Schema_impl中。

    实现类dd::Schema_impl主要实现了对于元数据对象的各属性的读写访问,与从数据字典中的元数据表schemata的行记录中,存取元数据的接口。

    主要相关接口如下:

    复制代码
    class Weak_object_impl_ : virtual public Weak_object {
      // ...
     public:
      // 存储记录到元数据表
      virtual bool store(Open_dictionary_tables_ctx *otx); 
     // 删除元数据表中的记录  bool drop(Open_dictionary_tables_ctx *otx) const;
     public:
      // 从元数据表的记录中提取各属性字段
      virtual bool restore_attributes(const Raw_record &r) = 0;
      // 保存各属性到元数据表的记录
      virtual bool store_attributes(Raw_record *r) = 0;
      // 读取相关对象的信息,如表上的索引等
      virtual bool restore_children(Open_dictionary_tables_ctx *) { return false; }
      // 存储相关对象的信息
      virtual bool store_children(Open_dictionary_tables_ctx *) { return false; }
      // 删除相关对象的信息
      virtual bool drop_children(Open_dictionary_tables_ctx *) const {
        return false;
      }
    };
    复制代码

    dd::Schema_impl主要实现了store_attributes和restore_attributes接口,依据dd::tables::Schemata中的表定义信息,读取或存储schema的各个属性信息。

    依据以上介绍的,数据字典表的类与数据库内部对象的类,结合InnoDB存储引擎的接口,实现了对于存储于数据字典各个表中的元数据的读写访问。

    例如,存储新建的database的元数据到schema内存对象中:

    复制代码
    #0  dd::Schema_impl::store_attributes
    #1  in dd::Weak_object_impl::store
    #2  in dd::cache::Storage_adapter::store
    #3  in dd::cache::Dictionary_client::store
    #4  in dd::create_schema
    #5  in mysql_create_db
    #6  in mysql_execute_command...
    复制代码

    持久化到对应的InnoDB表mysql.schemata中:

    复制代码
    #0  ha_innobase::write_row
    #1  in handler::ha_write_row
    #2  in dd::Raw_new_record::insert
    #3  in dd::Weak_object_impl::store
    #4  in dd::cache::Storage_adapter::store
    #5  in dd::cache::Dictionary_client::store
    #6  in dd::create_schema
    #7  in mysql_create_db
    #8  in mysql_execute_command
    ...
    复制代码

    数据字典的初始化

    初始化MySQL数据库实例时,即执行mysqld -initialize时,main函数会启动一个bootstrap线程来进行数据字典的初始化,并等待其完成。

    数据字典的初始化函数入口是dd::bootstrap::initialize,主要流程如下:

    图5 数据字典初始化流程

    其中,DDSE指的是Data Dictionary Storage Engine,数据字典的存储引擎,即InnoDB。DDSE初始化过程主要是对InnoDB进行必要的初始化,并获取DDSE代码中预先定义好的表的定义与表空间的定义。

    InnoDB预定义的数据字典表:

    • innodb_dynamic_metadata
    InnoDB的动态元数据,包括表的自增列值等。
    • innodb_table_stats
    InnoDB表的统计信息。
    • innodb_index_stats
    InnoDB索引的统计信息。
    • innodb_ddl_log
    存储InnoDB的DDL日志,用于原子DDL的实现。

    InnoDB预定义的系统表空间:

    • mysql
    数据字典的表空间,数据字典表都在这个表空间中。
    • innodb_system
    InnoDB的系统表空间,主要包含InnoDB的Change Buffer;如果不使用file-per-table或指定其他表空间,用户表也会创建在这个表空间中。

    InnoDB的ddse_dict_init接口的实现为innobase_ddse_dict_init,会先调用innobase_init_files初始化所需文件并启动InnoDB。

    主要代码流程如下:

    复制代码
    static bool innobase_ddse_dict_init(
        dict_init_mode_t dict_init_mode, uint,
     List<const dd::Object_table> *tables,List<const Plugin_tablespace> *tablespaces) {
    // ...
    // 初始化文件并启动InnoDB
    if (innobase_init_files(dict_init_mode, tablespaces)) {
    return true;
      }
    // innodb_dynamic_metadata表的定义
      dd::Object_table *innodb_dynamic_metadata =
          dd::Object_table::create_object_table();
      innodb_dynamic_metadata->set_hidden(true);
      dd::Object_table_definition *def =
          innodb_dynamic_metadata->target_table_definition();
      def->set_table_name("innodb_dynamic_metadata");
      def->add_field(0, "table_id", "table_id BIGINT UNSIGNED NOT NULL");
      def->add_field(1, "version", "version BIGINT UNSIGNED NOT NULL");
      def->add_field(2, "metadata", "metadata BLOB NOT NULL");
      def->add_index(0, "index_pk", "PRIMARY KEY (table_id)");
    // ...
    /* innodb_table_stats、innodb_index_stats、innodb_ddl_log表的定义 */
    // ...
    }
    复制代码

    在DDSE初始化并启动的基础上,就可以进行剩下的数据字典初始化过程,主要就是创建数据字典的schema和表。这些表的元数据在执行flush_meta_data时进行持久化。

    值得注意的是表mysql.dd_properties,它会存储版本信息等数据字典的属性,还会存储其他数据字典表的定义、id、se_private_data等信息,在数据库启动时使用。

    数据字典初始化整体执行的函数调用总结,如图6:

    6.PNG

    图6 数据字典初始化的函数调用

    数据字典的启动

    数据字典的启动过程所执行的函数与初始化时十分相似,大部分在函数内部通过opt_initialize全局变量来区分初始化和启动,执行不同的代码逻辑。

    与初始化的主要区别是元数据不再需要生成并持久化到存储,而是从存储读取已有的元数据。InnoDB文件是打开已有的,而不是新建。

    数据字典启动的入口是dd::upgrade_57::do_pre_checks_and_initialize_dd。这里虽然有'upgrade_57'这种名称的namespace,但是正常的启动也是从这里开始。

    与初始化相同,数据字典的启动也是先准备好DDSE,即启动InnoDB,然后再进行后面启动数据字典的步骤。打开数据字典之前,InnoDB会进行数据字典的恢复,确保重启前的DDL都正常的提交或回滚,数据字典元数据和数据是处于一致的状态。

    dd::upgrade_57::restart_dictionary调用dd::bootstrap::restart,后面的启动步骤由它来实现,主要过程如下。

    注意这里的创建表,是创建内存中的对象,不是物理上新创建一个表。这些表的元数据都已经在初始化时持久化了。

    复制代码
    bool restart(THD *thd) {
      bootstrap::DD_bootstrap_ctx::instance().set_stage(bootstrap::Stage::STARTED);
    // 获取预定义的系统tablespace的元数据(mysql和innodb_system)
      store_predefined_tablespace_metadata(thd);
    if (create_dd_schema(thd) ||  // 创建schema:'mysql'
          initialize_dd_properties(thd) ||  // 创建mysql.dd_properties表并从中获取版本号等信息 
         create_tables(thd, nullptr) ||  // 创建数据字典中其他的表
          sync_meta_data(thd) ||  // 从存储读取数据字典相关的schema、tablespace和表的元数据,进行同步
    /* 打开InnoDB的数据字典表(innodb_dynamic_metadata, innodb_table_stats, innodb_index_stats,
          innodb_ddl_log),加载所有InnoDB的表空间 */
          DDSE_dict_recover(thd, DICT_RECOVERY_RESTART_SERVER, 
                           d->get_actual_dd_version(thd)) ||      
          upgrade::do_server_upgrade_checks(thd) ||  // 检查是否能够升级(如果需要的话,正常启动不涉及)
          upgrade::upgrade_tables(thd) ||  // 升级数据字典表的定义及其中的元数据(如果需要的话,正常启动不涉及)
          repopulate_charsets_and_collations(thd) ||  // 更新charset和collation信息
          verify_contents(thd) ||  // 验证数据字典内容
          update_versions(thd, false)) {  // 更新版本信息到dd_properties表
    return true;
      }
    // ...
      bootstrap::DD_bootstrap_ctx::instance().set_stage(bootstrap::Stage::FINISHED);
      LogErr(INFORMATION_LEVEL, ER_DD_VERSION_FOUND, d->get_actual_dd_version(thd));
    return false;
    }
     
    复制代码

    启动时各个数据字典表的根页面信息是从 mysql.dd_properties表中获取的,通过该页面可以访问对应表的所有数据。

    mysql.dd_properties表的根页面是固定的,并且它里面保存了数组字典表本身的元数据。相关函数:dd::get_se_private_data()。

    小结

    MySQL 8.0新设计实现的数据字典,解决了之前版本的数据字典冗余,DDL原子性、crash safe等问题。通过对数据字典的初始化流程,以及数据字典正常重启时加载流程的梳理,希望读者对新数据字典的实现和运行有一个更深入的了解。

    后续会继续探究MySQL 8.0数据字典版本升级的内容,敬请期待。

    更多技术专栏内容:https://bbs.huaweicloud.com/blogs/427078

     

    点击关注,第一时间了解华为云新鲜技术~

  • 相关阅读:
    [C国演义] 第十三章
    数据库并发问题及四种隔离级别
    电脑案件冲突问题
    从零制作一个docker的镜像
    字节前端实习一面
    微信小程序开发--入门
    abap中程序跳转(全)
    26.ELK.针对‘ES中索引创建迟缓’问题优化
    sql 注入(1), union 联合注入
    计算机毕业设计Java校园快递代领系统(系统+源码+mysql数据库+lw文档)
  • 原文地址:https://www.cnblogs.com/huaweiyun/p/18264934
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号