😸

MySQLのメタデータロック機構を理解する

に公開

はじめに

こんにちは、M-Yamashitaです。

今回の記事では、MySQLのメタデータロック機構を理解する記事となります。ridgepoleを使ったマイグレーション実行時に、別クエリにてメタデータロックを取得していた時の挙動を調べることがあり、そもそもメタデータやメタデータロックとはなんなのかを深掘りしたいと思いました。
そのため私の知識の整理も兼ねて、この記事で共有します。

この記事で伝えたいこと

  • メタデータやメタデータロックの概要
  • メタデータロック取得の仕組み
  • 実際にメタデータロックを取得した際のmetadata_locksテーブルの内容について紹介

調査環境

  • MySQL 8.0.43

メタデータロック機構を知る

そもそもメタデータとは何か?

公式ドキュメントから引用します。

メタデータは「データに関するデータ」です。 データベースについて記述しているすべてのものがメタデータであり、データベースの内容ではありません。 したがって、カラム名、データベース名、ユーザー名、バージョン名、および SHOW の文字列結果のほとんどがメタデータです。

https://dev.mysql.com/doc/refman/8.0/ja/charset-metadata.html

つまりデータベース内に存在するテーブル内のレコードではなく、データベースの設定やテーブルの定義、ユーザーの権限などのデータを指すようです。

またMySQL 徹底入門の書籍にも記載がある通り、メタデータを閲覧できるデータベースが、INFORMATION_SCHEMAデータベースです。

MySQLデータベース内にあるオブジェクトのメタ情報を閲覧できるのが、INFORMATION_SCHEMAです。

引用: 日本MySQLユーザ会 yoku0825 坂井恵 鶴長鎮一 とみたまさひろ 深町日出海 福山裕大 斑石悦夫 山﨑由章 2020年7月6日発行 MySQL徹底入門 第4版 MySQL 8.0対応 翔泳社 p261

INFORMATION_SCHEMAが主にサーバーの静的な状態、すなわちメタデータを扱っていた

引用: 日本MySQLユーザ会 yoku0825 坂井恵 鶴長鎮一 とみたまさひろ 深町日出海 福山裕大 斑石悦夫 山﨑由章 2020年7月6日発行 MySQL徹底入門 第4版 MySQL 8.0対応 翔泳社 p264

このINFORMATION_SCHEMAには、テーブル定義に関する情報(TABLES)や、カラム定義に関する情報(COLUMNS)などが存在します。
一例として、INFORMATION_SCHEMA.TABLESにあるテーブルを確認してみます。確認結果をわかりやすくするため、usersテーブルを事前に作成しておきます。

CREATE TABLE users (
  id bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name varchar(255),
  email varchar(255),
  created_at datetime(6) NOT NULL,
  updated_at datetime(6) NOT NULL
)

次にINFORMATION_SCHEMA.TABLESを確認します。

mysql> SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'users';
+---------------+--------------------+------------+------------+--------------------+---------+------------+------------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+--------------------+----------+----------------+---------------+
| TABLE_CATALOG | TABLE_SCHEMA       | TABLE_NAME | TABLE_TYPE | ENGINE             | VERSION | ROW_FORMAT | TABLE_ROWS | AVG_ROW_LENGTH | DATA_LENGTH | MAX_DATA_LENGTH | INDEX_LENGTH | DATA_FREE | AUTO_INCREMENT | CREATE_TIME         | UPDATE_TIME | CHECK_TIME | TABLE_COLLATION    | CHECKSUM | CREATE_OPTIONS | TABLE_COMMENT |
+---------------+--------------------+------------+------------+--------------------+---------+------------+------------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+--------------------+----------+----------------+---------------+
| def           | performance_schema | users      | BASE TABLE | PERFORMANCE_SCHEMA |      10 | Fixed      |        128 |              0 |           0 |               0 |            0 |         0 |           NULL | 2025-10-12 22:53:18 | NULL        | NULL       | utf8mb4_0900_ai_ci |     NULL |                |               |
| def           | sample_development | users      | BASE TABLE | InnoDB             |      10 | Dynamic    |          0 |              0 |       16384 |               0 |            0 |         0 |              2 | 2025-10-12 23:58:37 | NULL        | NULL       | utf8mb4_0900_ai_ci |     NULL |                |               |
+---------------+--------------------+------------+------------+--------------------+---------+------------+------------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+--------------------+----------+----------------+---------------+
2 rows in set (0.03 sec)

usersテーブルがsample_developmentデータベースやperformance_schemaデータベースに存在することがわかります。

メタデータロックとは

メタデータロックについて公式ドキュメントから引用します。

MySQL では、メタデータロックを使用して、データベースオブジェクトへの同時アクセスを管理し、データの一貫性を確保します。

https://dev.mysql.com/doc/refman/8.0/ja/metadata-locking.html

メタデータロックは、以下のオブジェクトに対して適用されます。

  • テーブル
  • スキーマ(データベース)
  • ストアドプログラム(プロシージャ、ファンクション、トリガー、スケジュール済イベント)
  • テーブルスペース
  • GET_LOCK() 関数で取得されたユーザーロック
  • ロックサービスで取得されたロック
    • C言語上でのmysql_acquire_locking_service_locks関数で取得されたロック
    • SQL上でのユーザー定義関数(service_get_read_locksservice_get_write_locks)で取得されたロック
    • ロックサービスについては以下の公式ドキュメントを参照してください。

https://dev.mysql.com/doc/refman/8.0/ja/locking-service.html#locking-service-interface

メタデータロック取得・解放のタイミング

メタデータロック取得

ここについては、公式資料に記載があります。

特定のロックに複数の待機者がいる場合は、max_write_lock_count システム変数に関連する例外を除いて、優先度の高いロックリクエストが最初に満たされます。 書き込みロック要求の優先順位は、読み取りロック要求よりも高くなります。ただし、max_write_lock_count がある程度低い値 (たとえば、10) に設定されている場合、読み取りロック要求がすでに 10 個の書き込みロック要求を優先して渡されていれば、保留中の書き込みロック要求よりも読み取りロック要求が優先されることがあります。 通常、max_write_lock_count のデフォルト値は非常に大きいため、この動作は発生しません。

https://dev.mysql.com/doc/refman/8.0/ja/metadata-locking.html

このmax_write_lock_countシステム変数を実際に見てみると、次の表のようにデフォルト値が非常に高い数値となっていることがわかります。

Default Value (Windows) 4294967295
Default Value (Other, 64-bit platforms) 18446744073709551615
Default Value (Other, 32-bit platforms) 4294967295

https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html#sysvar_max_write_lock_count

そのため通常では、メタデータの書き込みロックの優先度が読み取りロックよりも高くなります。

優先度の高いロックリクエストが最初に満たされるとはどういうことでしょうか。公式ドキュメントでは以下のようなサンプルクエリを使用された例が示されていました。

クライアント 1:

LOCK TABLE x WRITE, x_new WRITE;

このステートメントは、x および x_new で名前順に書込みロックを要求および取得します。

クライアント 2:

INSERT INTO x VALUES(1);

ステートメントは、x で書込みロックを待機していることをリクエストおよびブロックします。

クライアント 3:

RENAME TABLE x TO x_old, x_new TO x;

このステートメントは、x、x_new および x_old で排他ロックを名前順に要求しますが、x でのロックの待機をブロックします。

クライアント 1:

UNLOCK TABLES;

このステートメントは、x および x_new の書込みロックを解放します。 クライアント 3 による x の排他ロックリクエストは、クライアント 2 による書込みロックリクエストよりも優先度が高いため、クライアント 3 は x でロックを取得し、x_new および x_old でも名前変更を実行してロックを解放します。 次に、クライアント 2 は x でロックを取得し、挿入を実行してロックを解放します。

なぜクライアント3の処理が優先となったのでしょうか。この優先度決定の仕組みについては、以下のコードで実装されているようです。

https://github.com/mysql/mysql-server/blob/2d6d5e10436a8f2b58d37af737c2a3e45855d0b7/sql/mdl.cc#L2396

このコードを元にすると、待機中のメタデータロックが複数あった場合、次に実行されるメタデータロックは以下の表の定義によって決定されるようです。

実行しようとするメタデータロックのタイプ 待機中となっているメタデータロックタイプで競合するタイプ
MDL_INTENTION_EXCLUSIVE なし
MDL_SHARED MDL_EXCLUSIVE
MDL_SHARED_HIGH_PRIO なし
MDL_SHARED_READ MDL_EXCLUSIVE、MDL_SHARED_NO_READ_WRITE
MDL_SHARED_WRITE MDL_EXCLUSIVE、MDL_SHARED_NO_READ_WRITE、MDL_SHARED_NO_WRITE
MDL_SHARED_WRITE_LOW_PRIO MDL_EXCLUSIVE、MDL_SHARED_NO_READ_WRITE、MDL_SHARED_NO_WRITE、MDL_SHARED_READ_ONLY
MDL_SHARED_UPGRADABLE MDL_EXCLUSIVE
MDL_SHARED_READ_ONLY MDL_EXCLUSIVE、MDL_SHARED_NO_READ_WRITE、MDL_SHARED_WRITE
MDL_SHARED_NO_WRITE MDL_EXCLUSIVE
MDL_SHARED_NO_READ_WRITE MDL_EXCLUSIVE
MDL_EXCLUSIVE なし

先ほどの公式ドキュメントのクエリを例にとると、次のような状況を考えてみます。

  • INSERT INTO x VALUES(1);MDL_SHARED_WRITEロックを要求
  • RENAME TABLE x TO x_old, x_new TO x;MDL_EXCLUSIVEロックを要求

これらのクエリが同じテーブルに対して同時に実行された場合、両方ともロックを取得できず待機状態になります。ここでUNLOCK TABLES;が実行されると、MySQLはどちらのロックを先に実行するか決める必要があります。この決定においてそれぞれのロックの観点で確認します。

MDL_SHARED_WRITEを実行しようとした場合
上記の表によると、MDL_SHARED_WRITEは以下のロックタイプと競合します:

  • MDL_EXCLUSIVE
  • MDL_SHARED_NO_READ_WRITE
  • MDL_SHARED_NO_WRITE

現在、待機リストにはMDL_EXCLUSIVEがあります。これは競合するロックタイプなので、MDL_SHARED_WRITEは実行できません。

MDL_EXCLUSIVEを実行しようとした場合

上記の表によると、MDL_EXCLUSIVEは競合するロックタイプがありません(表の「競合するタイプ」の欄が「なし」)。つまり、待機リストにMDL_SHARED_WRITEがあっても、MDL_EXCLUSIVEにとっては競合しないため、実行できます。

結果としてこのメカニズムにより、MDL_EXCLUSIVE(排他ロック)が優先的に実行されます。これは、テーブル構造を変更するRENAME TABLEのようなDDL操作が、データを読み書きするINSERTのようなDML操作よりも優先されることを意味します。

メタデータロック解放

公式文章から引用します。

トランザクションのシリアライザビリティーを確保するため、サーバーは、別のセッションで、未完了の明示的または暗黙的に開始されたトランザクションで使用されているテーブルに対して、セッションがデータ定義言語 (DDL) ステートメントを実行することを許可してはいけません。 サーバーは、トランザクション内で使用されているテーブルに対してメタデータロックを獲得し、トランザクションが終了するまでそれらのロックの解放を延期させることによって、これを実現します。
https://dev.mysql.com/doc/refman/8.0/ja/metadata-locking.html#metadata-lock-release

特筆すべきことはありませんが、トランザクションが終了するまでメタデータロックは解放されないということです。

実際のメタデータロック状況の確認方法

前提

docker composeで以下yamlファイルを作成し、MySQLを起動しておきます。

version: '3.8'

services:
  db:
    image: mysql:8.0
    platform: linux/amd64
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: sample_development
      TZ: 'Asia/Tokyo'
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
    command: >
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_bin
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      timeout: 20s
      retries: 10

確認

まず準備として約1GBのデータ(420万レコード)を持つテーブルを作成しておきます。この作成にはsysbenchコマンドを使用します。

sysbenchを使用した参考記事はこちらです。
https://tombo2.hatenablog.com/entry/2019/07/28/222203

MySQLはローカルコンテナ上で稼働させているので、ホストはそこを向くようにします。sysbenchコマンドの実行結果は以下の通りです。

> sysbench oltp_read_write --mysql-password=xxxxx --db-driver=mysql --mysql-db=sample_development --table-size=4200000 --tables=1 --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root prepare 

Creating table 'sbtest1'...
Inserting 4200000 records into 'sbtest1'
Creating a secondary index on 'sbtest1'...
>

次に、排他的なメタデータロックを取得するためにALTER TABLEクエリを実行します。インデックスを追加するクエリを実行します。

ALTER TABLE sample_development.sbtest1 ADD INDEX idx_pad(pad);

このインデックス追加でしばらく時間がかかるので、この間に別のターミナルでメタデータロックの状況を確認します。

ALTER TABLEクエリ実行前:

mysql> SELECT * FROM performance_schema.metadata_locks;
+-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+-------------------+-----------------+----------------+
| OBJECT_TYPE | OBJECT_SCHEMA      | OBJECT_NAME    | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE   | LOCK_DURATION | LOCK_STATUS | SOURCE            | OWNER_THREAD_ID | OWNER_EVENT_ID |
+-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+-------------------+-----------------+----------------+
| TABLE       | performance_schema | metadata_locks | NULL        |       140735142736832 | SHARED_READ | TRANSACTION   | GRANTED     | sql_parse.cc:6156 |            3332 |             10 |
+-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+-------------------+-----------------+----------------+
1 row in set (0.00 sec)

ALTER TABLEクエリ実行中:

mysql> SELECT * FROM performance_schema.metadata_locks;
+-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
| OBJECT_TYPE | OBJECT_SCHEMA      | OBJECT_NAME                | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE           | LOCK_DURATION | LOCK_STATUS | SOURCE             | OWNER_THREAD_ID | OWNER_EVENT_ID |
+-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
| GLOBAL      | NULL               | NULL                       | NULL        |       140734871145136 | INTENTION_EXCLUSIVE | STATEMENT     | GRANTED     | sql_base.cc:5476   |            3336 |             55 |
| BACKUP LOCK | NULL               | NULL                       | NULL        |       140734871196464 | INTENTION_EXCLUSIVE | TRANSACTION   | GRANTED     | sql_base.cc:5483   |            3336 |             55 |
| SCHEMA      | sample_development | NULL                       | NULL        |       140734872592336 | INTENTION_EXCLUSIVE | TRANSACTION   | GRANTED     | sql_base.cc:5463   |            3336 |             55 |
| TABLE       | sample_development | sbtest1                    | NULL        |       140734872410352 | SHARED_UPGRADABLE   | TRANSACTION   | GRANTED     | sql_parse.cc:6156  |            3336 |             55 |
| TABLESPACE  | NULL               | sample_development/sbtest1 | NULL        |       140734873166176 | INTENTION_EXCLUSIVE | TRANSACTION   | GRANTED     | lock.cc:807        |            3336 |             55 |
| TABLE       | sample_development | #sql-1_cd3                 | NULL        |       140734872610736 | EXCLUSIVE           | STATEMENT     | GRANTED     | sql_table.cc:17133 |            3336 |             55 |
| TABLE       | performance_schema | metadata_locks             | NULL        |       140735142736832 | SHARED_READ         | TRANSACTION   | GRANTED     | sql_parse.cc:6156  |            3332 |             11 |
+-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
7 rows in set (0.00 sec)

ALTER TABLEクエリ実行後:

mysql> SELECT * FROM performance_schema.metadata_locks; 
+-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+-------------------+-----------------+----------------+
| OBJECT_TYPE | OBJECT_SCHEMA      | OBJECT_NAME    | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE   | LOCK_DURATION | LOCK_STATUS | SOURCE            | OWNER_THREAD_ID | OWNER_EVENT_ID |
+-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+-------------------+-----------------+----------------+
| TABLE       | performance_schema | metadata_locks | NULL        |       140735142736832 | SHARED_READ | TRANSACTION   | GRANTED     | sql_parse.cc:6156 |            3332 |             12 |
+-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+-------------------+-----------------+----------------+
1 row in set (0.00 sec)

ALTER TABLE実行中にperformance_schema.metadata_locksテーブルでメタデータロックがかかっていることを確認できました。
では、この各レコードについて深掘りしていきます。

metadata_locksテーブルの出力結果の深掘り

metadata_locksテーブルにて、まずはOWNER_THREAD_IDを見てみます。このOWNER_THREAD_IDは、メタデータロックをリクエストしているスレッドIDを示しています。

https://dev.mysql.com/doc/refman/8.0/ja/performance-schema-metadata-locks-table.html

今回の例では3336と3332があり、これらはそれぞれ以下のスレッドを示しています。

  • OWNER_THREAD_IDが3336: ALTER TABLEクエリを実行しているスレッド
  • OWNER_THREAD_IDが3332: performance_schema.metadata_locksテーブルをSELECTしているスレッド

そのためOWNER_THREAD_IDが3336のスレッドとなっている各レコードに着目します。
(なお、以降の説明では、各行のカラム値のヘッダーがわかるように、カラム値のヘッダーを記載しています)

  • 1行目について

    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | OBJECT_TYPE | OBJECT_SCHEMA      | OBJECT_NAME                | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE           | LOCK_DURATION | LOCK_STATUS | SOURCE             | OWNER_THREAD_ID | OWNER_EVENT_ID |
    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | GLOBAL      | NULL               | NULL                       | NULL        |       140734871145136 | INTENTION_EXCLUSIVE | STATEMENT     | GRANTED     | sql_base.cc:5476   |            3336 |             55 |
    
    • OBJECT_TYPEGLOBALであるのでサーバー全体に対して、INTENTION_EXCLUSIVEのロックを取得しています。
      INTENTION_EXCLUSIVEはDDL実行中であることを示し、他のグローバルなDDLや管理操作を防ぐためのロックとなります。
    • そのためこの行では、"サーバー全体に対して、他のグローバルなDDLや管理操作を防ぐ"を意味します。
  • 2行目

    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | OBJECT_TYPE | OBJECT_SCHEMA      | OBJECT_NAME                | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE           | LOCK_DURATION | LOCK_STATUS | SOURCE             | OWNER_THREAD_ID | OWNER_EVENT_ID |
    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | BACKUP LOCK | NULL               | NULL                       | NULL        |       140734871196464 | INTENTION_EXCLUSIVE | TRANSACTION   | GRANTED     | sql_base.cc:5483   |            3336 |             55 |
    
    • OBJECT_TYPEBACKUP LOCKであり、LOCK_TYPEINTENTION_EXCLUSIVEとなっています。これはバックアップ操作中にDDLのようなテーブル構造の変更を防ぐためのロックを取得中であることを示します。
    • 補足: BACKUP LOCKはMySQL 8.0から導入された機能のようです。厳密には、Percona Serverで実現されていた機能を、MySQL 8.0で同様に実現するため、LOCK INSTANCE FOR BACKUP構文を使うことで実現されたようです。

https://blog.s-style.co.jp/2020/01/5650/

https://dev.mysql.com/doc/refman/8.0/ja/lock-instance-for-backup.html

  • 3行目

    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | OBJECT_TYPE | OBJECT_SCHEMA      | OBJECT_NAME                | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE           | LOCK_DURATION | LOCK_STATUS | SOURCE             | OWNER_THREAD_ID | OWNER_EVENT_ID |
    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | SCHEMA      | sample_development | NULL                       | NULL        |       140734872592336 | INTENTION_EXCLUSIVE | TRANSACTION   | GRANTED     | sql_base.cc:5463   |            3336 |             55 |
    
    • sample_developmentデータベースに対してINTENTION_EXCLUSIVEのロックを取得しています。そのためsample_developmentデータベースに対してDDLが実行中であることを示しており、これによって他のDDLや管理操作を防ぐことを意味します。
  • 4行目

    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | OBJECT_TYPE | OBJECT_SCHEMA      | OBJECT_NAME                | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE           | LOCK_DURATION | LOCK_STATUS | SOURCE             | OWNER_THREAD_ID | OWNER_EVENT_ID |
    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | TABLE       | sample_development | sbtest1                    | NULL        |       140734872410352 | SHARED_UPGRADABLE   | TRANSACTION   | GRANTED     | sql_parse.cc:6156  |            3336 |             55 |
    
    • sample_developmentデータベースのsbtest1テーブルに対してSHARED_UPGRADABLEのロック、つまり共有ロックを取得しています。つまりsbtest1テーブルに対して、他のセッションから読み取りや更新ができる状態です。
  • 5行目

    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | OBJECT_TYPE | OBJECT_SCHEMA      | OBJECT_NAME                | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE           | LOCK_DURATION | LOCK_STATUS | SOURCE             | OWNER_THREAD_ID | OWNER_EVENT_ID |
    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | TABLESPACE  | NULL               | sample_development/sbtest1 | NULL        |       140734873166176 | INTENTION_EXCLUSIVE | TRANSACTION   | GRANTED     | lock.cc:807        |            3336 |             55 |
    
    • OBJECT_TYPETABLESPACEとなっています。このTABLESPACEはInnoDBのデータを格納するファイルとなります。

    • またMySQLにはinnodb_file_per_tableオプションがあり、MySQL 5.6.6以降からデフォルトでONとなっています。これにより、CREATE TABLEクエリで作成したテーブルごとにデータを格納するファイルが作成されるようになります。

    • そのためこの5行目のレコードでは、このibdファイルに対するINTENTION_EXCLUSIVEのロックを取得することを意味しています。

      • 補足: 実際に私の環境でMySQLのデータを保存しているディレクトリを見てみると、次のようにテーブルごとのibdファイルが作成されていることがわかります。
      bash-5.1# pwd
      /var/lib/mysql/sample_development
      bash-5.1# ls
      sbtest1.ibd  users.ibd
      

https://dev.mysql.com/doc/refman/8.0/ja/innodb-file-space.html

https://qiita.com/SH2/items/654d89759e7e39d999b5

https://gihyo.jp/dev/serial/01/mysql-road-construction-news/0055

  • 6行目

    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | OBJECT_TYPE | OBJECT_SCHEMA      | OBJECT_NAME                | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE           | LOCK_DURATION | LOCK_STATUS | SOURCE             | OWNER_THREAD_ID | OWNER_EVENT_ID |
    +-------------+--------------------+----------------------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
    | TABLE       | sample_development | #sql-1_cd3                 | NULL        |       140734872610736 | EXCLUSIVE           | STATEMENT     | GRANTED     | sql_table.cc:17133 |            3336 |             55 |
    
    • この行ではsample_developmentデータベースにおいて、#sql-1_cd3というテーブルに対して排他ロックをかけているようです。この#sql-1_cd3というテーブル名については、MySQLの仕様により一時テーブルであると考えられます。
    • そのためこの6行目のレコードでは、sample_developmentデータベースにおいて、#sql-1_cd3という一時テーブルが作られており、この一時テーブルに対して排他ロックを取得していることを意味しています。

    一部のステートメントでは、MySQL によって、非表示ではなく、#sql で始まる名前を持つ一時 SQL テーブルが作成されます。

https://dev.mysql.com/doc/refman/8.0/ja/temporary-files.html

おわりに

この記事ではMySQLのメタデータとは何かから始まり、メタデータロックの概要や取得の仕組み、実際にメタデータロックを取得した際のmetadata_locksテーブルの内容について紹介しました。
記事を書く前まではなんとなく雰囲気で理解していましたが、実際にまとめていたことでより深く理解できたと思います。

この記事が誰かのお役に立てれば幸いです。

その他参考記事

https://qiita.com/arinc0/items/faef22a7c093e7a2d9ff

https://gihyo.jp/article/2022/09/mysql-rcn0180

Money Forward Developers

Discussion