MySQLの従来のレプリケーションでは、マスターからの更新があった場合、スレーブではシングルスレッドで処理されます。よって、マスターの並列での更新が多いと、スレーブで遅延が発生することがよくあります。最近のMySQLでは、スレーブでの実行を速くするために「マルチスレッドスレーブ」という機能が追加され、並列実行が可能になっています。
今回は、MySQL5.6から追加されたマルチスレッドスレーブ(MTS)について、MySQLのバージョンによる進化と共に紹介します。
MTSについて
前提として、MTSを有効化するには、スレーブでslave_parallel_workers
オプションに0より大きい数値を設定します。デフォルトは0(無効)です。
このオプションは、並列で実行するためのスレーブワーカースレッドの数を指定します。SQLスレッドは従来であればスレーブにSQLを実行していましたが、MTSの場合はスレーブワーカースレッドのコーディネータスレッドとして機能します。スレーブに対してSQLを適用するのは、このスレーブワーカースレッドになります。
この設定はオンラインで設定変更可能ですが、SQLスレッドの再起動が必要です。
performance_schema
のthreads
からスレーブワーカースレッドが起動しているかがわかります。
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を無効にするといいでしょう。