前回の投稿では、MySQLレプリケーションが良好な状態にあることを確認する方法について説明しました。また、典型的な問題のいくつかにも注目しました。この投稿では、MySQLレプリケーションを処理するときに発生する可能性のあるその他の問題について説明します。
エントリの欠落または重複
これは発生してはならないことですが、非常に頻繁に発生します。マスターで実行されたSQLステートメントは成功するが、スレーブの1つで実行された同じステートメントが失敗する状況です。主な理由はスレーブのドリフトです-何か(通常は誤ったトランザクションだけでなく、レプリケーションの他の問題やバグ)により、スレーブはマスターとは異なります。たとえば、マスターに存在していた行はスレーブには存在せず、削除または更新することはできません。この問題が発生する頻度は、主にレプリケーション設定によって異なります。つまり、MySQLがバイナリログイベントを保存する方法は3つあります。まず、「ステートメント」とは、マスターで実行されたのと同じように、SQLがプレーンテキストで記述されていることを意味します。この設定は、スレーブドリフトに対する許容度が最も高くなりますが、スレーブの一貫性を保証できない設定でもあります。本番環境での使用をお勧めすることは困難です。 2番目の形式「行」は、クエリステートメントの代わりにクエリ結果を格納します。たとえば、イベントは次のようになります。
### UPDATE `test`.`tab`
### WHERE
### @1=2
### @2=5
### SET
### @1=2
### @2=4
これは、「test」スキーマの「tab」テーブルの行を更新していることを意味します。最初の列の値は2で、2番目の列の値は5です。最初の列を2(値は変更されません)に設定し、2番目の列を2番目に設定します。ご覧のとおり、解釈の余地はあまりありません。使用される行とその変更方法が正確に定義されています。結果として、この形式はスレーブの一貫性には優れていますが、ご想像のとおり、データドリフトに関しては非常に脆弱です。それでも、MySQLレプリケーションを実行するための推奨される方法です。
最後に、3番目の「混合」は、ステートメントの形式で安全に記述できるイベントが「ステートメント」形式を使用するように機能します。データドリフトを引き起こす可能性のあるものは、「行」形式を使用します。
どのようにそれらを検出しますか?
いつものように、SHOWSLAVESTATUSは問題を特定するのに役立ちます。
Last_SQL_Errno: 1032
Last_SQL_Error: Could not execute Update_rows event on table test.tab; Can't find record in 'tab', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log binlog.000021, end_log_pos 970
Last_SQL_Errno: 1062
Last_SQL_Error: Could not execute Write_rows event on table test.tab; Duplicate entry '3' for key 'PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log binlog.000021, end_log_pos 1229
ご覧のとおり、エラーは明確で自明です(そして、MySQLとMariaDBの間で基本的に同じです。
問題をどのように修正しますか?
残念ながら、これは複雑な部分です。まず、信頼できる情報源を特定する必要があります。どのホストに正しいデータが含まれていますか?マスターまたはスレーブ?通常、それはマスターであると想定しますが、デフォルトでは想定しないでください。調査してください。フェイルオーバー後も、アプリケーションの一部が古いマスターに書き込みを発行し、現在はスレーブとして機能している可能性があります。そのホストでread_onlyが正しく設定されていないか、アプリケーションがスーパーユーザーを使用してデータベースに接続している可能性があります(はい、これは本番環境で見られます)。そのような場合、奴隷は真実の源になる可能性があります-少なくともある程度は。
どのデータを保持し、どのデータを使用するかによって、レプリケーションの同期を取り戻すために何が必要かを特定するのが最善の方法です。まず第一に、レプリケーションが壊れているので、これに注意する必要があります。マスターにログインし、レプリケーションが中断した場合でもバイナリログを確認します。
Retrieved_Gtid_Set: 5d1e2227-07c6-11e7-8123-080027495a77:1106672
Executed_Gtid_Set: 5d1e2227-07c6-11e7-8123-080027495a77:1-1106671
ご覧のとおり、5d1e2227-07c6-11e7-8123-080027495a77:1106672という1つのイベントがありません。マスターのバイナリログで確認しましょう:
mysqlbinlog -v --include-gtids='5d1e2227-07c6-11e7-8123-080027495a77:1106672' /var/lib/mysql/binlog.000021
#170320 20:53:37 server id 1 end_log_pos 1066 CRC32 0xc582a367 GTID last_committed=3 sequence_number=4
SET @@SESSION.GTID_NEXT= '5d1e2227-07c6-11e7-8123-080027495a77:1106672'/*!*/;
# at 1066
#170320 20:53:37 server id 1 end_log_pos 1138 CRC32 0x6f33754d Query thread_id=5285 exec_time=0 error_code=0
SET TIMESTAMP=1490043217/*!*/;
SET @@session.pseudo_thread_id=5285/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=1436549152/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=8/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
BEGIN
/*!*/;
# at 1138
#170320 20:53:37 server id 1 end_log_pos 1185 CRC32 0xa00b1f59 Table_map: `test`.`tab` mapped to number 571
# at 1185
#170320 20:53:37 server id 1 end_log_pos 1229 CRC32 0x5597e50a Write_rows: table id 571 flags: STMT_END_F
BINLOG '
UUHQWBMBAAAALwAAAKEEAAAAADsCAAAAAAEABHRlc3QAA3RhYgACAwMAAlkfC6A=
UUHQWB4BAAAALAAAAM0EAAAAADsCAAAAAAEAAgAC//wDAAAABwAAAArll1U=
'/*!*/;
### INSERT INTO `test`.`tab`
### SET
### @1=3
### @2=7
# at 1229
#170320 20:53:37 server id 1 end_log_pos 1260 CRC32 0xbbc3367c Xid = 5224257
COMMIT/*!*/;
最初の列を3に、2番目の列を7に設定する挿入であることがわかります。テーブルがどのように見えるかを確認しましょう:
mysql> SELECT * FROM test.tab;
+----+------+
| id | b |
+----+------+
| 1 | 2 |
| 2 | 4 |
| 3 | 10 |
+----+------+
3 rows in set (0.01 sec)
現在、どのデータを優先するかに応じて、2つのオプションがあります。マスターに正しいデータがある場合は、スレーブでid=3の行を削除するだけです。誤ったトランザクションが発生しないように、必ずバイナリロギングを無効にしてください。一方、正しいデータがスレーブにあると判断した場合は、マスターでREPLACEコマンドを実行して、現在の(3、7)から(3、10)の内容を修正するためにid=3の行を設定する必要があります。ただし、スレーブでは、レプリケーションを再開できるようにするために、現在のGTIDをスキップする必要があります(より正確には、空のGTIDイベントを作成する必要があります)。
スレーブの行を削除するのは簡単です:
SET SESSION log_bin=0; DELETE FROM test.tab WHERE id=3; SET SESSION log_bin=1;
空のGTIDの挿入はほとんど同じくらい簡単です:
mysql> SET @@SESSION.GTID_NEXT= '5d1e2227-07c6-11e7-8123-080027495a77:1106672';
Query OK, 0 rows affected (0.00 sec)
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
mysql> SET @@SESSION.GTID_NEXT=automatic;
Query OK, 0 rows affected (0.00 sec)
この特定の問題を解決する別の方法(マスターを真実のソースとして受け入れる限り)は、pt-table-checksumやpt-table-syncなどのツールを使用して、スレーブがマスターと一致していない場所とその内容を特定することです。スレーブを同期に戻すには、マスターでSQLを実行する必要があります。残念ながら、この方法はかなり重い側面です。マスターに多くの負荷が追加され、一連のクエリがレプリケーションストリームに書き込まれるため、スレーブの遅延やレプリケーション設定の一般的なパフォーマンスに影響を与える可能性があります。これは、同期する必要のある行がかなりの数ある場合に特に当てはまります。
最後に、いつものように、マスターからのデータを使用してスレーブを再構築できます。このようにして、スレーブが最新の最新データで確実に更新されるようにすることができます。これは、実際には必ずしも悪い考えではありません。pt-table-checksum/ pt-table-syncを使用して同期する多数の行について話している場合、レプリケーションパフォーマンス、全体的なCPU、およびI/Oに大きなオーバーヘッドが発生します。必要な負荷と工数。
ClusterControlを使用すると、マスターデータの新しいコピーを使用してスレーブを再構築できます。
整合性チェック
前の章で述べたように、一貫性は深刻な問題になる可能性があり、MySQLレプリケーションセットアップを実行しているユーザーに多くの頭痛の種を引き起こす可能性があります。 MySQLスレーブがマスターと同期していることを確認する方法と、それに対して何ができるかを見てみましょう。
一貫性のないスレーブを検出する方法
残念ながら、ユーザーがスレーブに一貫性がないことを知る一般的な方法は、前の章で説明した問題の1つに遭遇することです。これを回避するには、スレーブの整合性を事前に監視する必要があります。それがどのように行われるかを確認しましょう。
PerconaToolkitのツールpt-table-checksumを使用します。レプリケーションクラスターをスキャンし、不一致を特定するように設計されています。
sysbenchを使用してカスタムシナリオを作成し、スレーブの1つに少し矛盾を導入しました。重要なこと(私たちのようにテストしたい場合)は、以下のパッチを適用して、pt-table-checksumに「sbtest」スキーマを非システムスキーマとして認識させる必要があります。
--- pt-table-checksum 2016-12-15 14:31:07.000000000 +0000
+++ pt-table-checksum-fix 2017-03-21 20:32:53.282254794 +0000
@@ -7614,7 +7614,7 @@
my $filter = $self->{filters};
- if ( $db =~ m/information_schema|performance_schema|lost\+found|percona|percona_schema|test/ ) {
+ if ( $db =~ m/information_schema|performance_schema|lost\+found|percona|percona_schema|^test/ ) {
PTDEBUG && _d('Database', $db, 'is a system database, ignoring');
return 0;
}
最初に、次の方法でpt-table-checksumを実行します。
master:~# ./pt-table-checksum --max-lag=5 --user=sbtest --password=sbtest --no-check-binlog-format --databases='sbtest'
TS ERRORS DIFFS ROWS CHUNKS SKIPPED TIME TABLE
03-21T20:33:30 0 0 1000000 15 0 27.103 sbtest.sbtest1
03-21T20:33:57 0 1 1000000 17 0 26.785 sbtest.sbtest2
03-21T20:34:26 0 0 1000000 15 0 28.503 sbtest.sbtest3
03-21T20:34:52 0 0 1000000 18 0 26.021 sbtest.sbtest4
03-21T20:35:34 0 0 1000000 17 0 42.730 sbtest.sbtest5
03-21T20:36:04 0 0 1000000 16 0 29.309 sbtest.sbtest6
03-21T20:36:42 0 0 1000000 15 0 38.071 sbtest.sbtest7
03-21T20:37:16 0 0 1000000 12 0 33.737 sbtest.sbtest8
ツールの呼び出し方法に関する重要な注意事項がいくつかあります。まず、設定するユーザーはすべてのスレーブに存在する必要があります。必要に応じて、「-slave-user」を使用して、スレーブにアクセスするための特権の少ない他のユーザーを定義することもできます。説明する価値のあるもう1つのことは、pt-table-checksumと完全には互換性のない行ベースのレプリケーションを使用することです。行ベースのレプリケーションを使用している場合、pt-table-checksumは、サポートされている唯一の形式であるため、セッションレベルのバイナリログ形式を「ステートメント」に変更します。問題は、そのような変更が、マスターに直接接続されているスレーブの第1レベルでのみ機能することです。中間マスター(つまり、複数レベルのスレーブ)がある場合、pt-table-checksumを使用するとレプリケーションが中断する可能性があります。これが、デフォルトで、ツールが行ベースのレプリケーションを検出すると、終了してエラーを出力する理由です:
「レプリカslave1にはbinlog_formatROWがあり、pt-table-checksumによってレプリケーションが中断される可能性があります。ツールのドキュメントの「制限」セクションにある「行ベースのレプリケーションを使用したレプリカ」をお読みください。リスクを理解している場合は、-no-check-binlog-formatを指定して、このチェックを無効にしてください。」
1レベルのスレーブのみを使用したため、「-no-check-binlog-format」を指定して先に進むのが安全でした。
最後に、最大ラグを5秒に設定します。このしきい値に達すると、pt-table-checksumは、ラグをしきい値未満にするために必要な時間一時停止します。
出力からわかるように、
03-21T20:33:57 0 1 1000000 17 0 26.785 sbtest.sbtest2
テーブルsbtest.sbtest2で不整合が検出されました。
デフォルトでは、pt-table-checksumはチェックサムをpercona.checksumsテーブルに格納します。このデータは、Percona Toolkitの別のツールであるpt-table-syncに使用して、データの正確な違いを見つけるためにテーブルのどの部分を詳細にチェックする必要があるかを特定できます。
一貫性のないスレーブを修正する方法
上で述べたように、それを行うためにpt-table-syncを使用します。この例では、pt-table-checksumによって収集されたデータを使用しますが、pt-table-syncを2つのホスト(マスターとスレーブ)にポイントすることもでき、両方のホストのすべてのデータを比較します。間違いなく時間とリソースを消費するプロセスであるため、pt-table-checksumからのデータがすでにある限り、それを使用する方がはるかに優れています。これは、出力をテストするために実行した方法です:
master:~# ./pt-table-sync --user=sbtest --password=sbtest --databases=sbtest --replicate percona.checksums h=master --print
REPLACE INTO `sbtest`.`sbtest2`(`id`, `k`, `c`, `pad`) VALUES ('1', '434041', '61753673565-14739672440-12887544709-74227036147-86382758284-62912436480-22536544941-50641666437-36404946534-73544093889', '23608763234-05826685838-82708573685-48410807053-00139962956') /*percona-toolkit src_db:sbtest src_tbl:sbtest2 src_dsn:h=10.0.0.101,p=...,u=sbtest dst_db:sbtest dst_tbl:sbtest2 dst_dsn:h=10.0.0.103,p=...,u=sbtest lock:1 transaction:1 changing_src:percona.checksums replicate:percona.checksums bidirectional:0 pid:25776 user:root host:vagrant-ubuntu-trusty-64*/;
ご覧のとおり、結果としていくつかのSQLが生成されています。注意すべき重要な点は--replicate変数です。ここで何が起こるかは、pt-table-checksumによって生成されたテーブルをpt-table-syncにポイントすることです。また、マスターを指します。
SQLが意味をなすかどうかを確認するために、-printオプションを使用しました。生成されたSQLは、生成された時点でのみ有効であることに注意してください。実際にどこかに保存し、確認してから実行することはできません。 SQLに意味があるかどうかを確認し、その直後に--executeフラグを指定してツールを再実行するだけです。
master:~# ./pt-table-sync --user=sbtest --password=sbtest --databases=sbtest --replicate percona.checksums h=10.0.0.101 --execute
これにより、スレーブがマスターと同期するようになります。 pt-table-checksumで確認できます:
[email protected]:~# ./pt-table-checksum --max-lag=5 --user=sbtest --password=sbtest --no-check-binlog-format --databases='sbtest'
TS ERRORS DIFFS ROWS CHUNKS SKIPPED TIME TABLE
03-21T21:36:04 0 0 1000000 13 0 23.749 sbtest.sbtest1
03-21T21:36:26 0 0 1000000 7 0 22.333 sbtest.sbtest2
03-21T21:36:51 0 0 1000000 10 0 24.780 sbtest.sbtest3
03-21T21:37:11 0 0 1000000 14 0 19.782 sbtest.sbtest4
03-21T21:37:42 0 0 1000000 15 0 30.954 sbtest.sbtest5
03-21T21:38:07 0 0 1000000 15 0 25.593 sbtest.sbtest6
03-21T21:38:27 0 0 1000000 16 0 19.339 sbtest.sbtest7
03-21T21:38:44 0 0 1000000 15 0 17.371 sbtest.sbtest8
ご覧のとおり、sbtest.sbtest2テーブルにはもうdiffはありません。
このブログ投稿が有益でお役に立てば幸いです。 MySQLレプリケーションの詳細については、ここをクリックしてください。ご質問やご提案がございましたら、以下のコメントからお気軽にお問い合わせください。