今日はなにの日。

気になったこと勉強になったことのメモ。

今日は、MySQL 8.0.32で追加された「explain_format」を触ってみたの日。

目次

とある日

MySQL 8.0.32が1/17にGAされました。

リリースノートを見てて、気になった「explain_format」について調べてた記録です。

MySQL :: MySQL 8.0 Release Notes :: Changes in MySQL 8.0.32 (2023-01-17, General Availability)

アップデート内容

サーバ変数に新しく「explain_format」が追加されています。

EXPLAINしたときのフォーマットを定義できる変数です。

コマンドライン形式 --explain-format=format
紹介された 8.0.32
システム変数 explain_format
範囲 グローバル、セッション
動的 はい
SET_VARヒントが適用されます いいえ
タイプ 列挙
デフォルト値 TRADITIONAL
有効な値 JSON``TREE``DEFAULT

設定できる値は以下の値です。

  • TRADITIONAL:ステートメントFORMAT=TRADITIONALの一部として指定されているかのように、MySQL の従来のテーブルベースの出力を使用します 。EXPLAINこれは変数のデフォルト値です。
  • JSON: 指定されているかのように、JSON 出力形式を使用し FORMAT=JSONます。
  • TREE: 指定されているかのように、ツリーベースの出力形式を使用しFORMAT=TREEます。
  • DEFAULT``TRADITIONAL:まったく同じ効果 を持つことの同義語 。

普通にEXPLAINした場合。

(root@localhost) [test] 8.0.32 > explain select * from parent join child on parent.id = parent_id where parent_id > 2 \G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: parent
   partitions: NULL
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where; Using index
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: child
   partitions: NULL
         type: ref
possible_keys: par_ind
          key: par_ind
      key_len: 5
          ref: test.parent.id
         rows: 1
     filtered: 100.00
        Extra: NULL
2 rows in set, 1 warning (0.00 sec)

今までTREE形式の場合は、formatで指定が必要でした。

(root@localhost) [test] 8.0.32 > explain format=tree select * from parent join child on parent.id = parent_id where parent_id > 2 \G;
*************************** 1. row ***************************
EXPLAIN: -> Nested loop inner join  (cost=0.81 rows=1)
    -> Filter: (parent.id > 2)  (cost=0.46 rows=1)
        -> Covering index range scan on parent using PRIMARY over (2 < id)  (cost=0.46 rows=1)
    -> Index lookup on child using par_ind (parent_id=parent.id)  (cost=0.35 rows=1)

今回のアップデートでexplain_formatで指定した形式によって出力してくれます。

(root@localhost) [test] 8.0.32 > set explain_format = tree;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test] 8.0.32 > explain select * from parent join child on parent.id = parent_id where parent_id > 2 \G;
*************************** 1. row ***************************
EXPLAIN: -> Nested loop inner join  (cost=0.81 rows=1)
    -> Filter: (parent.id > 2)  (cost=0.46 rows=1)
        -> Covering index range scan on parent using PRIMARY over (2 < id)  (cost=0.46 rows=1)
    -> Index lookup on child using par_ind (parent_id=parent.id)  (cost=0.35 rows=1)

1 row in set (0.00 sec)

地味ですが便利な変更ですね。

explain_format見比べ

(root@localhost) [test] 8.0.32 > select @@explain_format;
+------------------+
| @@explain_format |
+------------------+
| TREE             |
+------------------+
1 row in set (0.00 sec)

TRADITIONAL

(root@localhost) [test] 8.0.32 > set explain_format = traditional;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test] 8.0.32 > select @@explain_format;
+------------------+
| @@explain_format |
+------------------+
| TRADITIONAL      |
+------------------+
1 row in set (0.00 sec)

(root@localhost) [test] 8.0.32 > explain select * from parent join child on parent.id = parent_id where parent_id > 2 \G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: parent
   partitions: NULL
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where; Using index
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: child
   partitions: NULL
         type: ref
possible_keys: par_ind
          key: par_ind
      key_len: 5
          ref: test.parent.id
         rows: 1
     filtered: 100.00
        Extra: NULL
2 rows in set, 1 warning (0.00 sec)

JSON

(root@localhost) [test] 8.0.32 > set explain_format = json;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test] 8.0.32 > select @@explain_format;
+------------------+
| @@explain_format |
+------------------+
| JSON             |
+------------------+
1 row in set (0.00 sec)

(root@localhost) [test] 8.0.32 > explain select * from parent join child on parent.id = parent_id where parent_id > 2 \G;
*************************** 1. row ***************************
EXPLAIN: {
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "0.81"
    },
    "nested_loop": [
      {
        "table": {
          "table_name": "parent",
          "access_type": "range",
          "possible_keys": [
            "PRIMARY"
          ],
          "key": "PRIMARY",
          "used_key_parts": [
            "id"
          ],
          "key_length": "4",
          "rows_examined_per_scan": 1,
          "rows_produced_per_join": 1,
          "filtered": "100.00",
          "using_index": true,
          "cost_info": {
            "read_cost": "0.36",
            "eval_cost": "0.10",
            "prefix_cost": "0.46",
            "data_read_per_join": "8"
          },
          "used_columns": [
            "id"
          ],
          "attached_condition": "(`test`.`parent`.`id` > 2)"
        }
      },
      {
        "table": {
          "table_name": "child",
          "access_type": "ref",
          "possible_keys": [
            "par_ind"
          ],
          "key": "par_ind",
          "used_key_parts": [
            "parent_id"
          ],
          "key_length": "5",
          "ref": [
            "test.parent.id"
          ],
          "rows_examined_per_scan": 1,
          "rows_produced_per_join": 1,
          "filtered": "100.00",
          "cost_info": {
            "read_cost": "0.25",
            "eval_cost": "0.10",
            "prefix_cost": "0.81",
            "data_read_per_join": "16"
          },
          "used_columns": [
            "id",
            "parent_id"
          ]
        }
      }
    ]
  }
}
1 row in set, 1 warning (0.00 sec)

TREE

(root@localhost) [test] 8.0.32 > set explain_format = tree;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test] 8.0.32 > select @@explain_format;
+------------------+
| @@explain_format |
+------------------+
| TREE             |
+------------------+
1 row in set (0.00 sec)

(root@localhost) [test] 8.0.32 > explain select * from parent join child on parent.id = parent_id where parent_id > 2 \G;
*************************** 1. row ***************************
EXPLAIN: -> Nested loop inner join  (cost=0.81 rows=1)
    -> Filter: (parent.id > 2)  (cost=0.46 rows=1)
        -> Covering index range scan on parent using PRIMARY over (2 < id)  (cost=0.46 rows=1)
    -> Index lookup on child using par_ind (parent_id=parent.id)  (cost=0.35 rows=1)

1 row in set (0.00 sec)

ANALYZE

(root@localhost) [test] 8.0.32 >  explain analyze select * from parent join child on parent.id = parent_id where parent_id > 2 \G;
*************************** 1. row ***************************
EXPLAIN: -> Nested loop inner join  (cost=0.81 rows=1) (actual time=0.022..0.063 rows=1 loops=1)
    -> Filter: (parent.id > 2)  (cost=0.46 rows=1) (actual time=0.014..0.040 rows=1 loops=1)
        -> Covering index range scan on parent using PRIMARY over (2 < id)  (cost=0.46 rows=1) (actual time=0.012..0.038 rows=1 loops=1)
    -> Index lookup on child using par_ind (parent_id=parent.id)  (cost=0.35 rows=1) (actual time=0.006..0.020 rows=1 loops=1)

1 row in set (0.00 sec)

DEFAULT

(TRADITIONALと同じだからこれなくてもいい気がする)

(root@localhost) [test] 8.0.32 > set explain_format = default;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test] 8.0.32 > select @@explain_format;
+------------------+
| @@explain_format |
+------------------+
| TRADITIONAL      |
+------------------+
1 row in set (0.00 sec)

エラー

EXPLAIN ANALYZEでJSON形式は対応していないのでエラーになります。

MySQL :: MySQL 8.0 Reference Manual :: 13.8.2 EXPLAIN Statement

(root@localhost) [test] 8.0.32 > set explain_format = json;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) [test] 8.0.32 >  explain analyze select * from parent join child on parent.id = parent_id where parent_id > 2 \G;
ERROR 1235 (42000): This version of MySQL doesn't yet support 'EXPLAIN ANALYZE with JSON format'
ERROR:
No query specified

おまけ

ドキュメント見てて8.0.33に関する記述を見つけた。

MySQL :: MySQL 8.0 リファレンスマニュアル :: 13.8.2 EXPLAIN ステートメント

MySQL 8.0.33 から、 EXPLAIN ANALYZEおよびの出力の数値はEXPLAIN FORMAT=TREE、次の規則に従ってフォーマットされます。

  • 0.001 ~ 999999.5 の範囲の数値は、10 進数として出力されます。

    1000 未満の 10 進数の有効数字は 3 桁です。残りは 4 つ、5 つ、または 6 つです。

  • 0.001 ~ 999999.5 の範囲外の数値は工学形式で出力されます。そのような値の例は 1.23e+9、 および934e-6です。

  • 末尾のゼロは出力されません。たとえば、 2.3ではなく2.30、では1.2e+6なく を印刷します1.20e+6

  • 未満の数値1e-12は として出力され 0ます。

個人的には、便利だと言われているTREE形式とか使わないでデフォルトでやるのであまり使わなさそうだなという感想です。