はじめに
2021 年 10 月 19 日に MySQL 8.0.27 がリリース されました。非同期レプリケーションにおける変更点の 1 つとして、デフォルトでマルチスレッドレプリカが有効になり、レプリカの遅延を軽減しやすくなることが期待できるようになりました。
Replication: Multithreading is now enabled by default for replica servers. A multithreaded applier has a number of applier threads that execute transactions in parallel. This behavior can avoid many cases of unwanted replication lag that can cause temporary divergence between the source and replicas.
具体的には、以下のシステム変数のデフォルト値がそれぞれ変更されています。
- replica_parallel_workers = 4 (MySQL 8.0.26 以前は 0)
- replica_preserve_commit_order = 1 (MySQL 8.0.26 以前は 0(OFF))
- replica_parallel_type = LOGICAL_CLOCK ((MySQL 8.0.26 以前は DATABASE)
今回は、今後デフォルトで有効になるこのマルチスレッドレプリカについて、運用時にどのような点に気を付ければ良いのか、代表的なものをいくつか紹介させていただこうかと思います。
マルチスレッドレプリカについて
まず、マルチスレッドレプリカとは MySQL 5.6 から実装された機能であり、レプリカにおけるレプリケーション SQL スレッドを増やして、レプリカで実行できるトランザクション数の並列化を有効にする機能です。有効にした状態でレプリケーションをおこなうと、以下のように複数のレプリケーション SQL スレッドが作成されるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 |
mysql> SHOW PROCESSLIST; +----+-----------------+-----------------+------+---------+------+----------------------------------------------------------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-----------------+-----------------+------+---------+------+----------------------------------------------------------+------------------+ (...) | 29 | system user | | NULL | Query | 8 | Replica has read all relay log; waiting for more updates | NULL | | 30 | system user | | NULL | Connect | 238 | Waiting for an event from Coordinator | NULL | | 31 | system user | | NULL | Connect | 8 | Waiting for an event from Coordinator | NULL | | 32 | system user | | NULL | Connect | 8 | Waiting for an event from Coordinator | NULL | | 33 | system user | | NULL | Connect | 8 | Waiting for an event from Coordinator | NULL | +----+-----------------+-----------------+------+---------+------+----------------------------------------------------------+------------------+ 8 rows in set (0.00 sec) |
この機能は、replica_parallel_workers を 0 よりも大きい値に設定することで有効になります。
これによって、レプリケーション SQL スレッドは指定された数のワーカースレッドとそれらを管理するためのコーディネータースレッドが作成されるようになり、リレーログの内容を並列に実行することが可能になっています。
また、MySQL 8.0.27 ではこの他にも replica_preserve_commit_order = 1 が設定されているため、レプリカにデータが反映される際にはソースと同じ順序でトランザクションが実行されるようになっており、並列実行してもデータの整合性を維持することができます。
トランザクションのリトライとタイムアウト
マルチスレッドレプリカでは複数のレプリケーション SQL スレッドが存在するため、これまでのシングルスレッドのレプリケーションとは異なり、レプリカサーバーで更新処理を実行していなくても、ソースサーバーのように行ロックやデッドロックが発生する可能性があります。
そのため、トランザクションの実行に失敗した際、自動的に replica_transaction_retries(デフォルト値 : 10) 回のリトライをおこなうようになっています。
ただし、たとえば非常に長いトランザクションが実行されていたり、あるいは以前のバージョンではバグの影響などにより、replica_transaction_retries を超えてリトライが失敗すると、以下のようにエラーとなってしまうので注意してください。
1 2 3 4 5 6 7 8 9 |
(...) [Warning] Slave SQL for channel '': Worker 1 failed executing transaction (...); Error 'Lock wait timeout exceeded; try restarting transaction' on query.(...) [Warning] Slave SQL for channel '': Worker 1 failed executing transaction (...); Error 'Lock wait timeout exceeded; try restarting transaction' on query.(...) [Warning] Slave SQL for channel '': Worker 1 failed executing transaction (...); Error 'Lock wait timeout exceeded; try restarting transaction' on query.(...) [Warning] Slave SQL for channel '': Worker 1 failed executing transaction (...); Error 'Lock wait timeout exceeded; try restarting transaction' on query.(...) [Warning] Slave SQL for channel '': Worker 1 failed executing transaction (...); Error 'Lock wait timeout exceeded; try restarting transaction' on query.(...) [Warning] Slave SQL for channel '': Worker 1 failed executing transaction (...); Error 'Lock wait timeout exceeded; try restarting transaction' on query.(...) [ERROR] Slave SQL for channel '': worker thread retried transaction 10 time(s) in vain, giving up. Consider raising the value of the slave_transaction_retries variable. Error_code: 1205 [ERROR] Slave SQL for channel '': Worker 1 failed executing transaction (...); Lock wait timeout exceeded; try restarting transaction, Error_code: 1205 |
このようなエラーが発生した場合は、手動でレプリケーション SQL スレッドを再開する必要があるため、エラーの内容に問題がないことを確認してから再開するようにしてください。
また、このリトライは行ロックやデッドロックのように一時的なエラーに対しては適用されますが、更新対象の行データが存在しない場合などには適用されません。
replica_pending_jobs_size_max による処理待ち
以前の記事「MTS(マルチスレッドスレーブ)を使う場合の注意点」 でも紹介させていただきましたが、マルチスレッドレプリカを使用する際には、replica_pending_jobs_size_max(デフォルト値 : 128M) の値に気を付ける必要があります。
マルチスレッドレプリカでは、ワーカースレッドに replica_pending_jobs_size_max を超える未処理のイベント(トランザクション)がきた場合、全てのワーカースレッドのキューが空になるまでイベントが待機状態になります。イベントの待機が発生すると、以下のようなメッセージが出力されます。
1 |
[Note] Multi-threaded slave: Coordinator has waited 611 times hitting replica_pending_jobs_size_max; current event size = 8169. |
ここで、レプリケーションにおける更新情報は、通常ソースサーバーおよびレプリカサーバーの max_allowed_packet(デフォルト値 : 64M) の値を上限としてやり取りされています。より正確には、レプリケーションスレッドに関しては replica_max_allowed_packet(デフォルト値 : 1G) が上限になっています。
もし replica_pending_jobs_size_max がこれらの値より小さかった場合、大きなトランザクションを実行するたびに待機が発生してしまい、レプリケーション遅延が頻繁に起きる可能性があります。そのため、各値は以下のように調整するようにしてください。
max_allowed_packet <= replica_pending_jobs_size_max <= replica_max_allowed_packet
ソースサーバーのバイナリログのポジションラグ(MTS ギャップ)
GTID を使用したレプリケーション の場合は影響ありませんが、マルチスレッドレプリカでは SHOW REPLICA における Exec_source_log_pos の情報は最低水準点、すなわち表示されている値まではコミットされていることが保証されていますが、それより後のトランザクションについてもコミットされている可能性があり、コミットされているかどうかについては保証されていません。
そのため、もしレプリカサーバーの SQL スレッドや mysqld プロセスが強制終了してから再起動すると、以下のようなエラーが発生してレプリケーションが再開できなくなる可能性があります。
1 2 3 4 |
[Note] Slave: MTS group recovery relay log info group_master_log_name mysql-bin.000001, event_master_log_pos 1187487. [ERROR] --relay-log-recovery cannot be executed when the slave was stopped with an error or killed in MTS mode; consider using RESET SLAVE or restart the server with --relay-log-recovery = 0 followed by START SLAVE UNTIL SQL_AFTER_MTS_GAPS [ERROR] Slave: Failed to initialize the master info structure for channel 'myhostrep'; its record may still be present in 'mysql.slave_master_info' table, consider deleting it. Aborting initialization of other channels. [ERROR] Failed to create or recover replication info repositories. |
このようなケースでは、START REPLICA UNTIL SQL_AFTER_MTS_GAPS を実行することによりバイナリログのポジションラグを解消することが可能です。実行後は、再度 START REPLICA を実行してレプリケーションを復帰させるようにしてください。
なお、relay_log_recovery が有効な場合は以下のように、再起動時に自動で START REPLICA UNTIL SQL_AFTER_MTS_GAPS が実行されるため、ポジションを確認した上で START REPLICA を実行すればレプリケーションを復旧できます。
1 2 3 4 |
[Note] Slave I/O thread for channel '': connected to master 'repl@192.168.174.3',replication started in log 'mysql-bin.000002' at position 624537 [Note] Slave SQL thread for channel '' initialized, starting replication in log 'mysql-bin.000002' at position 624537, relay log './relay-bin.000004' position: 4 [Note] Slave SQL thread stopped according to UNTIL SQL_AFTER_MTS_GAPS as it has processed all gap transactions left from the previous slave session. [Note] Slave SQL thread for channel '' exiting, replication stopped in log 'mysql-bin.000002' at position 624537 |
また、ソースサーバーのバイナリログの正確なポジション情報は mysql.slave_worker_info にバイナリ形式で格納されており、STOP REPLICA や mysqld プロセスの正常終了時、START REPLICA UNTIL SQL_AFTER_MTS_GAPS 実行時などの際はこの情報をもとにポジションラグを解消しているので、特に気を付ける必要はありません。
ただし、mysqldump の –dump-slave オプションを利用してソースサーバーのバイナリログのポジション情報を取得しようとした場合、ポジションラグによって正確な情報が取得できない可能性があります。
そのため、上記のオプションを利用する場合は、以下のように一度マルチスレッドレプリカを停止してからバックアップを取得する必要があるので注意してください。
1 2 3 |
mysql> STOP REPLICA; mysql> SET GLOBAL replica_parallel_workers = 0; mysql> START REPLICA; |
まとめ
ここまで、マルチスレッドレプリカにおける運用時の注意事項について代表的なものをいくつか紹介してきました。
マルチスレッドレプリカは MySQL 5.6 から実行されましたが当時はあまり有用ではなく、それ以降徐々に機能が拡充してきて利用しやすくなり、ようやく MySQL8.0.27 でデフォルトになったことで感慨深くもあります。ただし、マルチスレッドレプリカ特有の注意事項なども存在しているので、新しく導入される際には最低限今回の内容に注意して利用してもらえればと思います。
参照リファレンス
Changes in MySQL 8.0.27 (2021-10-19, General Availability)
17.2.3.2 レプリケーションアプライアンスワーカースレッドの監視
17.5.1.32 レプリケーション再試行とタイムアウト
17.5.1.34 レプリケーションとトランザクションの非一貫性