MySQL・8.0新特性・New data dictionary試食編

23351 ワード

周知のように、MySQLは統合サーバ層+異なる下位エンジンプラグインのアーキテクチャモードを採用しているため、サーバ層でテーブルごとにfrmファイルを作成し、テーブル定義に関連するメタデータ情報を保存します.しかし、一部のエンジン(例えばInnoDB)自体にもメタデータが格納されているため、メタデータの冗長性だけでなく、サーバ層とエンジン層がそれぞれ管理されているため、DDLなどの操作を実行する際にcrash-safeを実行することは難しく、DDLにトランザクション性を持たせることは言うまでもありません.
これらの問題(特にDDLではatomicができない)を解決するためにMySQL 8から.0からFRMファイルおよび他のserverレイヤのメタデータファイル(frm,par,trn,trg,isl,db.opt)がキャンセルされ、すべてのメタデータがInnoDBエンジンで格納され、権限テーブルなどのシステムテーブルもInnoDBエンジンに変更されます.
本文は筆者が初めてこの内容を理解したので、あまり深く入り込むことはありません.関連する変更が多すぎるため、後で暇があれば一つ一つ展開します.
本明細書のすべてのテストおよびコード関連部分はMySQL 8に基づいている.0.0バージョンは、8.0バージョンの最初の開発バージョンであるため、将来の動作が変化することは排除されません.
テスト
まず、新しいライブラリを作成し、ライブラリの下に2つのテーブルを作成してテストを開始します.
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ファイルはなく、データディレクトリの下で、このsbtestライブラリの情報を記述するためにSDIファイルが生成されていることがわかります.
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テーブルt 1を作成し、対応するSDIファイルが作成され、このテーブルの詳細がJSON形式で記録される.公式ファイルの説明によれば、このファイルの存在は、まだ完全に実現されていない機能のためである.
新しいInformation Schema定義
いくつかの新しい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      。

Data Dictionary Cache
データ辞書の構造が大きく変化すると、それに応じてメモリデータ辞書Cacheも変更され、
mysql> show variables like '%defin%';
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| schema_definition_cache         | 256   |
| stored_program_definition_cache | 256   |
| table_definition_cache          | 1400  |
| tablespace_definition_cache     | 256   |
+---------------------------------+-------+
4 rows in set (0.00 sec)

tablespace_definition_Cache:tablespace cacheのサイズで、tablespaceの定義が格納.1つのtablespaceに複数のtableが含まれる場合があります.
table_definition_cache:
stored_program_definition_Cache:ストレージプロセス&&functionの定義cache.
schema_definition_Cache:schema定義のcacheを格納する
hardcodeの文字セットcache:
character set definition cache partition: Stores character set definition objects and has a hardcoded object limit of 256.
collation definition cache partition: Stores collation definition objects and has a hardcoded object limit of 256.

システムテーブルの変化
  • 権限に関するテーブルをInnoDBエンジン
  • に変換する.
    //含む:user,db,tables_priv, columns_priv, procs_priv, proxies_priv
    //公式ブログ紹介
  • funcテーブルをInnoDBトランザクションテーブル
  • に変換
    //この変更により、CREATE FUNCTIONまたはDROP FUNCTION、またはユーザー定義UDFなどのfunctionの操作が暗黙的にコミットされる可能性があります.
  • mysqlライブラリの下にあるroutineテーブルおよびeventテーブルは使用されなくなり、これらの情報は新しいDD tableに格納され、mysqlライブラリの下では表示されません.
  • 外部キーシステム表
  • //2つの非表示システムテーブルforeign_を使用keysとforeign_key_column_USageは外部キー情報を格納する//この2つのシステムテーブルが表示されないため、ISライブラリの下のREFERENTIAL_を通過する必要があります.CONTRAINTSとKEY_COLUMN_USAGEテーブルは、外部キー情報//導入の互換性を取得します.foreign keyの名前は64文字を超えてはいけません(以前のバージョンでは許可されていました)
    ソースコードの概要
    ソースディレクトリに戻ると、serverレイヤからNew DDを管理するために大量の新しいコードファイルが導入され、主に一連の統一APIが定義され、コードはsql/ddディレクトリの下に保存され、関数とクラス定義はnamespace ddの下にある.
    異なるメタデータに対して、異なるクラスとその継承関係を定義します.
    namespace dd {
        Weak_object
            Entity_object   
                Dictionary_object
                    Tablespace
                    Schema
                    Event
                    Routine
                        Function
                        Procedure
                    Charset
                    Collation
                    Abstract_table
                        Table
                    Spatial_reference_system
                    Index_stat
                        View
                    Table_stat
                Partition
                Trigger
                Index
                Foreign_key
                Parameter
                Column
            Partition_index 
            Partition_value
            View_routine
            View_table
            Tablespace_file
            Foreign_key_element
            Index_element
            Column_type_element
            Parameter_type_element
        Object_table
            Dictionary_object_table
        Object_type
        Object_table_definition   
        
    }

    データ辞書Cache管理クラス:
    dd::cache {
        dd::cache::Dictionary_client
        Object_registry
        Element_map
        Multi_map_base
            Local_multi_map
            Shared_multi_map
        
        Cache_element
        Free_list
        Shared_dictionary_cache
        
        Storage_adapter
    }

    mysqlライブラリにはシステムテーブルが格納されていますが、show tablesコマンドでは37個のテーブルしか表示されません.ディスクから見るとmysqlディレクトリのibdファイルは37個をはるかに超えています.これは、一部のシステムテーブルがユーザーに対して見えないことを意味します.これらのテーブルもコアデータ辞書の情報を管理するために使用されています.見えないのは、ユーザーの不適切な操作を避けるためです.(もちろん、将来のこの動作の変化も排除されない)、これらのテーブルへのアクセスについては、ディレクトリsql/dd/impl/tables/においてインタフェース定義が行われ、これらの非表示のテーブルは、以下を含む.
    $grep 'std::string s_table_name' sql/dd/impl/tables/* | awk '{ print $4}'
    s_table_name("catalogs");
    s_table_name("character_sets");
    s_table_name("collations");
    s_table_name("columns");
    s_table_name("column_type_elements");
    s_table_name("events");
    s_table_name("foreign_key_column_usage");
    s_table_name("foreign_keys");
    s_table_name("index_column_usage");
    s_table_name("indexes");
    s_table_name("index_partitions");
    s_table_name("index_stats");
    s_table_name("parameters");
    s_table_name("parameter_type_elements");
    s_table_name("routines");
    s_table_name("schemata");
    s_table_name("st_spatial_reference_systems");
    s_table_name("table_partitions");
    s_table_name("table_partition_values");
    s_table_name("tables");
    s_table_name("tablespace_files");
    s_table_name("tablespaces");
    s_table_name("table_stats");
    s_table_name("triggers");
    s_table_name("version");
    s_table_name("view_routine_usage");
    s_table_name("view_table_usage");

    1つのテーブルに対する一般的な操作を例に、コードの一部がどのように呼び出されているかを見てみましょう.(New DDのコードの変更が大きいため、関連するworklogは数十個あり、筆者はテスト+コードdebugの方式を通じて第一歩でコードを熟知し、記録の比較的乱れている)
    ライブラリレベルのアクション
  • database
  • を作成する
    mysql> create database db1;
    Query OK, 1 row affected (2.87 sec)
    
    mysql> create database db2;
    Query OK, 1 row affected (3.05 sec)

    エントリ関数:mysql_create_db--databaseディレクトリの作成--binlogを構築してファイルに書き込む--DD APIインタフェースを呼び出す:dd::create_schema
    *     dd::Schema
    *         mysql.schemata  ,    :
    
    dd::create_schema
    |--> dd::cache::Dictionary_client::store<:schema>
        |--> dd::cache::Storage_adapter::store<:schema>
            |--> dd::Weak_object_impl::store
                |--> dd::Raw_new_record::insert
                
    
    Note: schemata         
        mysql> desc schemata;
        ERROR 3554 (HY000): Access to system table 'mysql.schemata' is rejected.
    
    *             SDI   ,sdi          ,    
        
    dd::create_schema
    |--> dd::store_sdi
        |--> dd::sdi_file::store
            |--> write_sdi_file
    
        
    *    commit,   rollback
    
  • database
  • を修正
    mysql> alter database db1 default charset gbk;
    Query OK, 1 row affected (2 min 17.54 sec)

    エントリ関数:mysql_alter_db--DD APIインタフェースの呼び出し:dd::alter_schema
    *         ,    :
    
    dd::alter_schema
    |--> dd::cache::Dictionary_client::update<:schema>
        |--> dd::cache::Dictionary_client::store<:schema>
            |--> dd::cache::Storage_adapter::store<:schema> 
                |--> dd::Weak_object_impl::store
                    |--> dd::Raw_record::update
    
    *  sdi  ,     
    
    dd::alter_schema
    |--> dd::Sdi_updater::operator()
        |--> dd::update_sdi
            |--> dd::sdi_file::store
                |--> write_sdi_file
    *     ,          ?? (8.0.0  ,why ??)
       sdi          ,          ,               ,      ,     SDI  
    ref: http://bugs.mysql.com/bug.php?id=83281
    

    --Binlogを書く
  • show databases
  • mysql> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | db1                |
    | db2                |
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    +--------------------+
    6 rows in set (1.40 sec)

    このコマンドを実行すると、実際にSQL変換が行われ、標準のクエリー文に変換されます.スタックは次のとおりです.
    dispatch_command
    |-->mysql_parse
        |-->parse_sql
            |-->MYSQLparse
                |--> dd::info_schema::build_show_databases_query

    変換後のSQL類似:
           SELECT SCHEMA_NAME as `Database`,
              FROM information_schema.schemata;
    

    システム・テーブルから直接読み込むため、データ・ディレクトリの下にフォルダを作成しても新しいデータベース・ディレクトリとはみなされません.
  • database
  • を削除
    mysql> drop database db2;
    Query OK, 0 rows affected (1 min 1.86 sec)
    

    --関連ファイルを削除
    --システムテーブルmysql/schemataのレコードを削除
    mysql_rm_db
    |--> dd::drop_schema
        |--> dd::cache::Dictionary_client::drop<:schema>
            |-->dd::cache::Storage_adapter::drop<:schema>
                |--> dd::Weak_object_impl::drop
                    |--> dd::Raw_record::drop
                        |--> handler::ha_delete_row

    表レベルのアクション
  • テーブル
  • を作成
    mysql> create table t1 (a int primary key, b int, c int, key(b));
    Query OK, 0 rows affected (7 min 12.29 sec)

    エントリ関数:
    mysql_create_table_no_lock
    |--> create_table_impl
        |--> rea_create_table

    --ddに新しいレコードを挿入します(dd::create_table-->dd::create_dd_user_table).
    //          `dd::Table`   ,       ,       ,    
    //       
    dd::create_dd_user_table
    |-->  dd::cache::Dictionary_client::store<:table> 
        |-->dd::cache::Storage_adapter::store<:table>
            |-->dd::Weak_object_impl::store
                //     mysql/tables    
                
                //           , "mysql/columns", 
                |-->dd::Table_impl::store_children
                    |--> dd::Abstract_table_impl::store_children      // mysql/columns
                        |--> dd::Collection<:column>::store_items
                            |--> Weak_object_impl::store
                |-->dd::Collection<:index>::store_items           // mysql/indexes
                    |--> dd::Weak_object_impl::store
                        |-->dd::Index_impl::store_children
                            |--> dd::Collection<:index_element>::store_items    // mysql/index_column_usage

    --エンジンファイルを作成します
  • Open table

  • --インスタンスを再起動してからテーブルを開きます.テーブル定義は初めてメモリにロードされます.システムテーブルにアクセスしてテーブル定義を取得する必要があります.
    open_and_process_table
    |-->open_table
        |-->get_table_share_with_discover
            |-->get_table_share
                |-->open_table_def
                    //   schema    ,     `mysql/schemata`    cache 
                    |-->dd::schema_exists   
                        |--> dd::cache::Dictionary_client::acquire<:schema> 
                            |-->dd::cache::Dictionary_client::acquire<:item_name_key dd::schema="">
                                |-->dd::cache::Shared_dictionary_cache::get<:item_name_key dd::schema="">
                                    |-->dd::cache::Shared_dictionary_cache::get_uncached<:item_name_key dd::schema="">
                                        |-->dd::cache::Storage_adapter::get<:item_name_key dd::schema="">
                                            |-->dd::Raw_table::find_record
                    //             mysql/tables  
                    |-->dd::abstract_table_type
                        |-->dd::cache::Dictionary_client::acquire<:abstract_table>
                            |-->dd::cache::Dictionary_client::acquire<:item_name_key dd::abstract_table="">
                                |-->dd::cache::Shared_dictionary_cache::get<:item_name_key dd::abstract_table="">
                                    |-->dd::cache::Shared_dictionary_cache::get_uncached<:item_name_key dd::abstract_table="">
                                        |-->dd::cache::Storage_adapter::get<:item_name_key dd::abstract_table="">
                                            |--> dd::Raw_table::find_record
                                            //          
                                            |-->Dictionary_object_table_impl::restore_object_from_record
                                                |-->dd::Table_impl::restore_children
                                                    |-->dd::Abstract_table_impl::restore_children
                                                        //  mysql/columns        
                                                        |-->dd::Collection<:column>::restore_items<:abstract_table_impl>
                                                        //  mysql/indexs         
                                                        |-->dd::Collection<:index>::restore_items<:table_impl>
                                                        // mysql/index_column_usage      
                                                        |-->dd::Collection<:index_element>::restore_items<:index_impl>
                                                        //  mysql/foreign_keys      
                                                        |-->dd::Collection<:foreign_key>::restore_items<:table_impl>
                                                        //  mysql/table_partitions      
                                                        |-->dd::Collection<:partition>::restore_items<:table_impl>
                                                        // "mysql/triggers       
                                                        |-->dd::Collection<:trigger>::restore_items<:table_impl/>

    関連ワークログ
    WL#6379: Schema definitions for new DDWL#6380: Formulate framework for API for DDWL#6381: Handler API changes for new dictionaryWL#6382: Define and Implement API for Table objectsWL#6383: Define and Implement API for TriggersWL#6384: Define and Implement API for Stored RoutinesWL#6385: Define and Implement API for SchemaWL#6387: Define and Implement API for TablespacesWL#6388: Define and Implement API for EventsWL#6389: Define and Implement API for ViewsWL#6390: Use new DD API for handling non-partitioned tablesWL#6391: Protect Data Dictionary tablesWL#6392: Upgrade to Transactional Data DictionaryWL#6394: Bootstrap code for new DDWL#6416: InnoDB: Remove the use of *.isl filesWL#6599: New Data Dictionary and I_S integrationWL#6929: Move FOREIGN KEY constraints to the global data dictionaryWL#7053: InnoDB: Provide storage for tablespace dictionaryWL#7066: External tool to extract InnoDB tablespace dictionary informationWL#7069: Provide data dictionary information in serialized formWL#7167: Change DDL to update rows for view columns in DD.COLUMNS and other dependent values.WL#7284: Implement common code for different DD APIsWL#7464: InnoDB: provide a way to do non-locking readsWL#7488: InnoDB startup refactoringWL#7630: Define and Implement API for Table Partition InfoWL#7771: Make sure errors are properly handled in DD APIWL#7784: Store temporary table metadata in memoryWL#7836: Use new DD API for handling partitioned tablesWL#7896: Use DD API to work with triggersWL#7897: Use DD API to work with stored routinesWL#7898: Use DD API to work with eventsWL#7907: Runtime: Use non-locking reads for DD tables under I_S view.WL#8150: Dictionary object cacheWL#8433: Separate DD commands from regular SQL queries in the parser grammarWL#8980: Move UDF table from MyISAM to Transactional StorageWL#9045: Make user management DDLs atomic
    公式ブログ:https://mysqlserverteam.com/mysql-server-bootstrapping-and-dictionary-initialization/https://mysqlserverteam.com/bootstrapping-the-transactional-data-dictionary/