MySQL道普請便り

第79回MySQLのマルチスレッドスレーブ

MySQLの従来のレプリケーションでは、マスターからの更新があった場合、スレーブではシングルスレッドで処理されます。よって、マスターの並列での更新が多いと、スレーブで遅延が発生することがよくあります。最近のMySQLでは、スレーブでの実行を速くするために「マルチスレッドスレーブ」という機能が追加され、並列実行が可能になっています。

今回は、MySQL5.6から追加されたマルチスレッドスレーブ(MTS)について、MySQLのバージョンによる進化と共に紹介します。

MTSについて

前提として、MTSを有効化するには、スレーブでslave_parallel_workersオプションに0より大きい数値を設定します。デフォルトは0(無効)です。

このオプションは、並列で実行するためのスレーブワーカースレッドの数を指定します。SQLスレッドは従来であればスレーブにSQLを実行していましたが、MTSの場合はスレーブワーカースレッドのコーディネータスレッドとして機能します。スレーブに対してSQLを適用するのは、このスレーブワーカースレッドになります。

この設定はオンラインで設定変更可能ですが、SQLスレッドの再起動が必要です。

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

mysql> STOP SLAVE SQL_THREAD;
Query OK, 0 rows affected (0.00 sec)

mysql> START SLAVE SQL_THREAD;
Query OK, 0 rows affected (0.00 sec)

performance_schemathreadsからスレーブワーカースレッドが起動しているかがわかります。

mysql> SELECT name FROM threads WHERE name LIKE '%slave_worker%';
+-------------------------+
| NAME                    |
+-------------------------+
| thread/sql/slave_worker |
| thread/sql/slave_worker |
| thread/sql/slave_worker |
| thread/sql/slave_worker |
+-------------------------+

slave_workerがスレーブワーカースレッドです。

MySQL 5.6のMTS

MySQL5.6でのMTSはデータベース単位で並列化されます。

たとえば、データベースの数が3つ存在する場合は、slave_parallel_workers=3とすることで3つのワーカースレッドが起動され、それぞれのデータベースに対して並列で実行されるようになります。データベースの数が1つしか存在しない、またはデータベースは2つ存在しているけどよく使用されるのは1つのデータベースだけ、といった場合はMTSの恩恵は薄いでしょう。

またMySQL 5.6のMTSでは、タイミングによってはトランザクションの実行順序がマスターとは異なる可能性があります。マスターとスレーブの整合性は結果整合性となります。

MySQL 5.7のMTS

MySQL5.7でのMTSは前述の方式に加えて、LOGICAL_CLOCK方式が追加されました。

この方式は、コミットのタイムスタンプベースで判断し、同一のデータベースであっても、マスターでバイナリログのグループコミットされたトランザクションにおいては並列化して実行されます。グループコミットとは、コミットした時間がほとんど同時であった複数のトランザクションをまとめて1回の書き込み操作にすることで、スループットを向上させる機能です。

slave_parallel_typeオプションが追加され、LOGICAL_CLOCKを設定することで動作します。デフォルトはDATABASEで、動作は前述のMySQL 5.6のMTSの動作となります。

MySQL 5.7.22以降 または MySQL 8.0のMTS

MySQL5.7.22以降またはMySQL8.0では、WRITESET方式による並列化することが可能になりました。

binlog_transaction_dependency_trackingオプションが追加され、WRITESETまたはWRITESET_SESSIONと設定することで動作します。デフォルトはCOMMIT_ORDERで、前述のMySQL 5.7のMTSの動作となります。また、transaction_write_set_extractionオプションをXXHASH64に設定する必要があります。

この方式はトランザクションの依存関係を追跡、ロギングして、同じ行を更新しないトランザクションを並列に実行する仕組みです。

注意事項

MTSではリレーログから実行された一連のトランザクションにギャップが生じることがあります。ギャップとは、マスターで実行されたトランザクションの順番がMTSによって順番が入れ替わったトランザクションを指します。それを防ぐにはMySQL5.7とそれ以降では可能で、以下の設定を行います。

  • slave_preserve_commit_order=1
  • slave_parallel_type=LOGICAL_CLOCK
  • log-bin
  • log-slave-updates

この設定をすることで、マスターで実行された順序通りにスレーブで実行されます。実行されるトランザクションはコミットする前にすべての以前のトランザクションがコミットされるまで待機します。

また、以下のような障害が発生して、ギャップが生じてSTART SLAVEできないことがあります。

  • スレーブワーカースレッドが強制終了した場合
  • コーディネータスレッドが強制終了した場合
  • MySQLが強制終了した場合

その場合は、START SLAVE UNTIL SQL_AFTER_MTS_GAPSステートメントを使用して、ギャップを解消させる必要があります。その後、RESET SLAVEを実施して解決することができます。詳しくはマニュアル:13.4.2.6 START SLAVE Syntaxを参照ください。

ちなみに、MTS環境下ではSQL_AFTER_MTS_GAPS 以外のSTART SLAVE UNTIL構文は使用できません。

ギャップの発生は防ぐことはできましたが、MTS環境下では一部問題があります。それは、SHOW SLAVE STATUSを表示したときのExec_master_log_posは実行された箇所よりも以前の箇所にある可能性があります。この問題は現状では防ぐことはできません。

たとえば、MTS環境下のスレーブから--dump-slaveを使用したmysqldumpコマンドにてバックアップを取得し、新たにスレーブを作成する場合に、記録されたポジションからSTART SLAVEすると、すでに適用済のトランザクションから開始される可能性があります。

GTIDベースのレプリケーションであれば、すでに適用済のトランザクションは無視されるため問題ありませんが、ポジションベースの場合はエラーとなる可能性があります。

ポジションベースの場合は、バックアップ取得する際にMTSを無効にするといいでしょう。

mysql> STOP SLAVE;
mysql> SET GLOBAL slave_parallel_workers=0;
mysql> START SLAVE;

おすすめ記事

記事・ニュース一覧