この記事は MySQL 8.0: all you need to know about SDI (2021/8/12掲載)の翻訳版です。本記事に関する最新情報は別途ご確認ください。

MySQL8.0 ではMyISAMテーブルに .frm ファイルが.sdi ファイルに置き換わりました。(参照).

しかし、このファイルは何なのでしょうか?MyISAMテーブル以外の用途に使用できるのでしょうか?…これらの疑問について確認しながら.sdiファイルについてより深く理解しましょう。

SDIとは何の略でしょうか?

SDISerialized Dictionary Information(シリアライズディクショナリ情報) の略です。

ご存知のように、MySQL 8.0では、テーブルのメタデータやその構造を保存する方法が、(InnoDB内の)新しいトランザクションデータディクショナリに置き換えられました。

さらに、これらの情報はInnoDBテーブルスペースの一部として追加されるため、メタデータとデータは一緒に保存されます。

この機能をサポートしていないストレージエンジンでは、外部ファイルすなわちSDIファイルが作成されます。例えばMyISAMテーブルの場合です。

どのように見えるのでしょうか?

ストレージエンジンがテーブルのメタデータを保存する機能を持っていない場合、SDIファイルが作成されます。それはどのようなものでしょうか?

SDIファイルの実体はコンパクトなJSONファイルで、ファイル名はテーブル名とテーブルIDで構成されます。

以前の記事で紹介したテーブルを例にとると、ファイルシステム上では、SDIファイルは次のようになります。

[root@imac my_isam_db]# ls -l *.sdi
-rw-r----- 1 mysql mysql 3600 Aug  2 12:49 t1_5017.sdi

5017 はテーブルIDになります。

実際には、ディクショナリのオブジェクトID(OID)を文字列で表現したものです。OIDは一意性を保証するもので、複数のテーブルが同じ名前にマッピングされる可能性があるため、必要なものです。

では実際のJSONがどのようなものか見てみましょう。

{
  "mysqld_version_id": 80026,
  "dd_version": 80023,
  "sdi_version": 80019,
  "dd_object_type": "Table",
  "dd_object": {
    "name": "t1",
    "mysql_version_id": 80026,
    "created": 20210802104936,
    "last_altered": 20210802104936,
    "hidden": 1,
    "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": "id",
        "type": 4,
        "is_nullable": false,
        "is_zerofill": false,
        "is_unsigned": true,
        "is_auto_increment": true,
        "is_virtual": false,
        "hidden": 1,
        "ordinal_position": 1,
        "char_length": 10,
        "numeric_precision": 10,
        "numeric_scale": 0,
        "numeric_scale_null": false,
        "datetime_precision": 0,
        "datetime_precision_null": 1,
        "has_no_default": false,
        "default_value_null": false,
        "srs_id_null": true,
        "srs_id": 0,
        "default_value": "AAAAAA==",
        "default_value_utf8_null": true,
        "default_value_utf8": "",
        "default_option": "",
        "update_option": "",
        "comment": "",
        "generation_expression": "",
        "generation_expression_utf8": "",
        "options": "interval_count=0;",
        "se_private_data": "",
        "engine_attribute": "",
        "secondary_engine_attribute": "",
        "column_key": 2,
        "column_type_utf8": "int unsigned",
        "elements": [],
        "collation_id": 8,
        "is_explicit_collation": false
      },
      {
        "name": "name",
        "type": 16,
        "is_nullable": true,
        "is_zerofill": false,
        "is_unsigned": false,
        "is_auto_increment": false,
        "is_virtual": false,
        "hidden": 1,
        "ordinal_position": 2,
        "char_length": 20,
        "numeric_precision": 0,
        "numeric_scale": 0,
        "numeric_scale_null": true,
        "datetime_precision": 0,
        "datetime_precision_null": 1,
        "has_no_default": false,
        "default_value_null": true,
        "srs_id_null": true,
        "srs_id": 0,
        "default_value": "",
        "default_value_utf8_null": true,
        "default_value_utf8": "",
        "default_option": "",
        "update_option": "",
        "comment": "",
        "generation_expression": "",
        "generation_expression_utf8": "",
        "options": "interval_count=0;",
        "se_private_data": "",
        "engine_attribute": "",
        "secondary_engine_attribute": "",
        "column_key": 1,
        "column_type_utf8": "varchar(20)",
        "elements": [],
        "collation_id": 8,
        "is_explicit_collation": false
      },
      {
        "name": "inserted",
        "type": 18,
        "is_nullable": false,
        "is_zerofill": false,
        "is_unsigned": false,
        "is_auto_increment": false,
        "is_virtual": false,
        "hidden": 1,
        "ordinal_position": 3,
        "char_length": 19,
        "numeric_precision": 0,
        "numeric_scale": 0,
        "numeric_scale_null": true,
        "datetime_precision": 0,
        "datetime_precision_null": 0,
        "has_no_default": false,
        "default_value_null": false,
        "srs_id_null": true,
        "srs_id": 0,
        "default_value": "AAAAAA==",
        "default_value_utf8_null": false,
        "default_value_utf8": "CURRENT_TIMESTAMP",
        "default_option": "CURRENT_TIMESTAMP",
        "update_option": "",
        "comment": "",
        "generation_expression": "",
        "generation_expression_utf8": "",
        "options": "interval_count=0;",
        "se_private_data": "",
        "engine_attribute": "",
        "secondary_engine_attribute": "",
        "column_key": 1,
        "column_type_utf8": "timestamp",
        "elements": [],
        "collation_id": 8,
        "is_explicit_collation": false
      }
    ],
    "schema_ref": "my_isam_db",
    "se_private_id": 18446744073709552000,
    "engine": "MyISAM",
    "last_checked_for_upgrade_version_id": 0,
    "comment": "",
    "se_private_data": "",
    "engine_attribute": "",
    "secondary_engine_attribute": "",
    "row_format": 2,
    "partition_type": 0,
    "partition_expression": "",
    "partition_expression_utf8": "",
    "default_partitioning": 0,
    "subpartition_type": 0,
    "subpartition_expression": "",
    "subpartition_expression_utf8": "",
    "default_subpartitioning": 0,
    "indexes": [
      {
        "name": "PRIMARY",
        "hidden": false,
        "is_generated": false,
        "ordinal_position": 1,
        "comment": "",
        "options": "flags=0;",
        "se_private_data": "",
        "type": 1,
        "algorithm": 2,
        "is_algorithm_explicit": false,
        "is_visible": true,
        "engine": "MyISAM",
        "engine_attribute": "",
        "secondary_engine_attribute": "",
        "elements": [
          {
            "ordinal_position": 1,
            "length": 4,
            "order": 2,
            "hidden": false,
            "column_opx": 0
          }
        ]
      }
    ],
    "foreign_keys": [],
    "check_constraints": [],
    "partitions": [],
    "collation_id": 8
  }
}

ご覧のように、このSDIファイルには多くの情報が含まれています。
参考までに、テーブル作成時のコマンドは以下の通りです。

CREATE TABLE `t1` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `inserted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

SDI ファイルは MyISAM でのみ使用されるのでしょうか?

SDIファイルはIMPORT TABLE FROMステートメントを使用して、MySIAMテーブルをMySQL 8.0 サーバーにコピーするために使用することができます。

また、SDIファイルはメタデータ情報を埋め込むことができない他のストレージエンジンにも使用されます。現在はInnoDB と NDB だけが SDI ファイルを必要としないストレージエンジンになります。

以下のように、CSVテーブルを作成した場合、そのテーブルもSDIを持つことになります。

CREATE TABLE `t2` (
  `id` int unsigned NOT NULL,
  `name` varchar(20) NOT NULL DEFAULT '',
  `inserted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=CSV DEFAULT CHARSET=latin1

[root@imac my_isam_db]# ls -l t2*
-rw-r----- 1 mysql mysql 3268 Aug 11 13:27 t2_5037.sdi
-rw-r----- 1 mysql mysql   35 Aug 11 13:27 t2.CSM
-rw-r----- 1 mysql mysql    0 Aug 11 13:27 t2.CSV

もちろん、SDIファイルからもその情報を確認することが可能です。

[root@imac my_isam_db]# cat t2_5037.sdi | jq ."dd_object"."engine"
"CSV"

SDI ファイルはメタデータの冗長性を提供します。また、InnoDBテーブルの場合でも、例えばデータディクショナリが使用できなくなった際にオブジェクトメタデータをInnoDBテーブルスペースファイルから直接抽出することが可能です。

SDIとInnoDB

InnoDBテーブルの場合、SDI情報はibd2sdiというツールを使ってテーブルスペースから抽出することができます。

ibd2sdi は file-per-table テーブルスペースファイル (*.ibd ファイル)、general tablespaceファイル (*.ibd files)、system tablespaceファイル (ibdata* files)、およびデータディクショナリテーブルスペース (mysql.ibd)に対して実行できます。一時テーブルスペースまたは undo テーブルスペースでの使用はサポートされていません。

では、InnoDBで試してみましょう。先ほどと同じように、InnoDBをストレージエンジンとして使用する別のテーブル((t3))を作成すると、実際にスキーマのディレクトリに1つの.ibdファイルが確認できます。

[root@imac my_isam_db]# ls
t1_5017.sdi  t1.MYD  t1.MYI  t2_5037.sdi  t2.CSM  t2.CSV  t3.ibd

そこからSDIファイルを生成します。

# ibd2sdi t3.ibd -d t3.sdi

生成したSDIファイルを解析することができますが、MySQLでは必須ではありません。これはメタデータのコピーにしかすぎません。

既存の SDI ファイルを手動で変更することはできますか?

もちろん、これは推奨されません。テーブルがすでにMySQLに登録された後に.sdiファイルを削除または修正した場合、すでにデータディクショナリの一部となっているため、MySQLサーバーは手動での変更は必要としません。

SDI ファイルを必要とするエンジンの場合はテーブルを変更するたびに、古いファイルは異なる OID を持つ新しいファイルに置き換えられます。