MySQL道普請便り

第192回MySQLのパラレル操作について

MySQLでは一般的に、ユーザーからのクエリやDDLなどの操作はシングルスレッドで処理されます。現行のMySQLでは、PostgreSQLやOracle Databaseが提供しているようなパラレルクエリはサポートしていません。しかし、MySQL 8.0とそれ以降から、InnoDBへの一部の操作がパラレルで処理されるようになりました。そのため、1つの操作に対して複数のCPUを効率的に利用することで、高速に処理することができるようになります。

今回は、パラレル操作と関連するパラメーターについて紹介したいと思います。なお、利用しているMySQLのバージョンは、8.0.32になります。

パラレル読み取り

MySQL 8.0.14とそれ以降から、オプションinnodb_parallel_read_threadsが追加されました。このオプションは、InnoDBのクラスタインデックスのページを同時に読み取るスレッドの数を指定します。セカンダリインデックスの読み取りには適用されません。デフォルトは4で、4つスレッドを利用しパラレル読み取りを実行します。

パラレル読み取りは、以下のステートメントで利用されます。

  • CHECK TABLEステートメント
  • WHERE句などの条件指定のない SELECT COUNT(*)ステートメント

パラレル読み取りの実行中は、専用のスレッドが起動されます。performance_schema.threadsテーブルからパラレル読み取り用スレッドthread/innodb/parallel_read_threadを確認することができます。クエリが完了すると、このスレッドは終了して、表示から消えます。

それでは、例としてSELECT COUNT(*)ステートメントを実行してみましょう。

mysql> SET innodb_parallel_read_threads=4;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT COUNT(*) FROM t0 ;
+----------+
| count(*) |
+----------+
| 12582912 |
+----------+
1 row in set (1.42 sec)

このステートメント実行中にperformance_schema.threadsテーブルを確認すると、パラレル読み取り用スレッドthread/innodb/parallel_read_threadが起動されていることがわかります。

mysql> SELECT 
  THREAD_ID,NAME,PROCESSLIST_STATE,PROCESSLIST_INFO 
  FROM performance_schema.threads 
  WHERE NAME IN ('thread/sql/one_connection' , 'thread/innodb/parallel_read_thread') 
  AND thread_id != PS_CURRENT_THREAD_ID();
+-----------+------------------------------------+-------------------+-------------------------+
| THREAD_ID | NAME                               | PROCESSLIST_STATE | PROCESSLIST_INFO        |
+-----------+------------------------------------+-------------------+-------------------------+
|       568 | thread/sql/one_connection          | executing         | SELECT COUNT(*) FROM t0 |
|      1258 | thread/innodb/parallel_read_thread | NULL              | NULL                    |
|      1259 | thread/innodb/parallel_read_thread | NULL              | NULL                    |
|      1260 | thread/innodb/parallel_read_thread | NULL              | NULL                    |
|      1261 | thread/innodb/parallel_read_thread | NULL              | NULL                    |
+-----------+------------------------------------+-------------------+-------------------------+

innodb_parallel_read_threadsを1にすると、パラレル読み取り用スレッドは起動しません。

mysql> SET innodb_parallel_read_threads=1;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT COUNT(*) FROM t0 ;
+----------+
| count(*) |
+----------+
| 12582912 |
+----------+
1 row in set (4.09 sec)
mysql> SELECT 
  THREAD_ID,NAME,PROCESSLIST_STATE,PROCESSLIST_INFO 
  FROM performance_schema.threads 
  WHERE NAME IN ('thread/sql/one_connection' , 'thread/innodb/parallel_read_thread') 
  AND thread_id != PS_CURRENT_THREAD_ID();
+-----------+---------------------------+-------------------+-------------------------+
| THREAD_ID | NAME                      | PROCESSLIST_STATE | PROCESSLIST_INFO        |
+-----------+---------------------------+-------------------+-------------------------+
|       568 | thread/sql/one_connection | executing         | SELECT COUNT(*) FROM t0 |
+-----------+---------------------------+-------------------+-------------------------+

実行時間も、パラレル読み取りスレッドが1の場合と4の場合を比較すると、4の方が速くなっていることがわかります。また8.0.17とそれ以降から、パーティションテーブルへのパラレル読み取りがサポートされています。

パラレルインデックス作成

MySQL 8.0.27とそれ以降から、オプションinnodb_ddl_threadsが追加されました。このオプションは、セカンダリインデックスの作成時のソートと、ソート済みデータのロードステージに対するスレッドの最大数を指定します。デフォルトは4で、最大4つのスレッドまで利用します。パラレルインデックス作成中は、performance_schema.threadsテーブルから専用スレッドthread/innodb/ddl_threadを確認することができます。

インデックス作成には、主に以下のようなステージを処理していきます。

順番 処理 ステージ名
1 クラスターインデックスの読み取りと一時ソートファイルへのデータの書き込み alter table (read PK and internal sort)
2 データのソート alter table (merge sort)
3 一時ソートファイルからセカンダリインデックスへソート済みデータのロード alter table (insert)
4 ALTER TABLEステートメントの実行中に実行されたDMLの適用 alter table (log apply index)
5 ALTER TABLEステートメントの終了処理 alter table (end)

パラレルインデックス作成は、前述のオプションinnodb_parallel_read_threadsとオプションinnodb_ddl_threadsを利用して実行しています。

alter table (read PK and internal sort)ステージでオプションinnodb_parallel_read_threadsのパラレル読み取りを利用し、alter table (merge sort)alter table (insert)ステージでオプションinnodb_ddl_threadsを利用することでインデックス作成を高速化しています。

たとえば、以下のインデックスを追加するステートメントを流してみます。

mysql> ALTER TABLE t0 ADD KEY idx_col1(col1);
Query OK, 0 rows affected (36.56 sec)
Records: 0  Duplicates: 0  Warnings: 0

インデックス作成中に、performance_schema.threadsテーブルを数回SELECTした結果が以下になります。

リスト クエリ
mysql> SELECT THREAD_ID,NAME,PROCESSLIST_STATE,PROCESSLIST_INFO 
FROM performance_schema.threads 
WHERE NAME IN ('thread/sql/one_connection' , 'thread/innodb/parallel_read_thread','thread/innodb/ddl_thread') AND thread_id != PS_CURRENT_THREAD_ID();
リスト 結果
+-----------+------------------------------------+-----------------------------------------+---------------------------------------+
| THREAD_ID | NAME                               | PROCESSLIST_STATE                       | PROCESSLIST_INFO                      |
+-----------+------------------------------------+-----------------------------------------+---------------------------------------+
|      2154 | thread/innodb/parallel_read_thread | NULL                                    | NULL                                  |
|      2155 | thread/innodb/parallel_read_thread | NULL                                    | NULL                                  |
|      2156 | thread/innodb/parallel_read_thread | NULL                                    | NULL                                  |
|      2157 | thread/innodb/parallel_read_thread | NULL                                    | NULL                                  |
|      1816 | thread/sql/one_connection          | alter table (read PK and internal sort) | ALTER TABLE t0 ADD KEY idx_col1(col1) |
+-----------+------------------------------------+-----------------------------------------+---------------------------------------+

+-----------+---------------------------+-----------------------------------------+---------------------------------------+
| THREAD_ID | NAME                      | PROCESSLIST_STATE                       | PROCESSLIST_INFO                      |
+-----------+---------------------------+-----------------------------------------+---------------------------------------+
|      2173 | thread/innodb/ddl_thread  | NULL                                    | NULL                                  |
|      2174 | thread/innodb/ddl_thread  | NULL                                    | NULL                                  |
|      2175 | thread/innodb/ddl_thread  | alter table (merge sort)                | NULL                                  |
|      1816 | thread/sql/one_connection | alter table (read PK and internal sort) | ALTER TABLE t0 ADD KEY idx_col1(col1) |
+-----------+---------------------------+-----------------------------------------+---------------------------------------+

+-----------+---------------------------+--------------------------+---------------------------------------+
| THREAD_ID | NAME                      | PROCESSLIST_STATE        | PROCESSLIST_INFO                      |
+-----------+---------------------------+--------------------------+---------------------------------------+
|      2173 | thread/innodb/ddl_thread  | NULL                     | NULL                                  |
|      2174 | thread/innodb/ddl_thread  | NULL                     | NULL                                  |
|      2175 | thread/innodb/ddl_thread  | alter table (insert)     | NULL                                  |
|      1816 | thread/sql/one_connection | alter table (merge sort) | ALTER TABLE t0 ADD KEY idx_col1(col1) |
+-----------+---------------------------+--------------------------+---------------------------------------+

インデックス作成の初期にalter table (read PK and internal sort)ステージでthread/innodb/parallel_read_threadが起動され、パラレル読み取りが動作しているのがわかります。その後、thread/innodb/parallel_read_threadは閉じられます。次にalter table (merge sort)alter table (insert)ステージでthread/innodb/ddl_threadが起動され、動作してることがわかります。

このように、セカンダリインデックスの作成の特定のステージはパラレルで動作するようになりました。

注意点として、以下のようなセカンダリインデックス作成においてはパラレル操作がサポートされていません。

  • 仮想列へのインデックス作成
  • フルテキストインデックス作成
  • 空間インデックス作成

まとめ

MySQL 8.0から利用できるパラレル操作について紹介しました。まだパラレルで可能な操作は限定的ですが、今後増えていくことを期待したいです。

今回紹介した内容をもっと詳しく知りたい方は、以下のドキュメントをご確認ください。

おすすめ記事

記事・ニュース一覧