メリークリスマイエスキューエル! (と、1日)
この記事は GMOペパボエンジニア Advent Calendar 2020 の26日目の記事のつもりです。
ちなみに私の中の人は GMOペパボではない会社 に勤めています。
最近 pt-table-checksum
にお世話になる機会が多くなって、使い方をまとめておこうと思ったメモです。
公式ドキュメントはこちら。
pt-table-checksum
自体は簡単に説明した昔の記事が出てきた。
シンプルな例えにすると、 pt-table-checksum
はこんな感じに binlog_format=STATEMENT
にしてクエリーを実行します。
mysql57 19> SET SESSION binlog_format = STATEMENT;
Query OK, 0 rows affected (0.00 sec)
mysql57 19> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql57 19> SELECT COUNT(*) FROM t1;
+----------+
| COUNT(*) |
+----------+
| 3 |
+----------+
1 row in set (0.00 sec)
mysql57 19> INSERT INTO checksum (table_name, master_cnt, this_cnd) SELECT 't1', 3, COUNT(*) FROM t1;
binlog_format=STATEMENT
を押し込むことで INSERT INTO .. SELECT ..
は「 master_cnt
はリテラルでマスターの値がそのまま入り、 this_cnt
はレプリカで SELECT
が再実行されるためレプリカでの値が入る」ことになります。
master_crc
, this_crc
も同じように「マスターで計算済みのリテラルを入れるカラムとレプリカでリプレイされて計算された値を入れるカラム」に分けられて記録されます。
チェック対象のテーブルはチャンクに分けられて実行されます。1チャンクが1トランザクションで処理されるため、各チャンクをまたいだデータの一貫性はありません(2チャンク目を計算している間に1チャンク目の行の数が変わっているかもしれない、ということ)
バイナリログ直列化の恩恵を受けられるため、マスターの1チャンク目とレプリカの1チャンク目は「同じスナップショット」であることが期待できます。先の例だと、マスターで1チャンク目を更新したあとに1チャンク目に新しく行が追加されても、レプリカはその新しい行が追加される前のスナップショットでチャンクを再計算するため、よほどレアなケースでない限り(もともと binlog_format = STATEMENT
&& 非決定性関数が頻繁に使われている環境でない限り)計算済みのチャンクで新たな不整合が発生する可能性は低いはずです。
チャンク分けしているのはたぶん、 INSERT .. SELECT ..
は SELECT
対象のテーブルの行に共有ロックを置くためロックの範囲が大きくなりすぎないようにとか、そんなにでっかくするとチェックサムを計算するのにレプリケーションが遅れるからとかそういうのの配慮だと思います。
で、俺のよく使うオプションはこんな感じでした。
$ pt-table-checksum \
> --host xxxx --port 3306 \
> --user pt_tcs --password 'xxxx' \
> --ignore-databases mysql,sys \
> --replicate pt.pt_tcs \
> --chunk-size=1000 \
> --recursion-method=processlist \
> --no-check-binlog-format \
> --pause-file=/tmp/pt_tcs.pause \
> --truncate-replicate-table
--host
, --port
はマスターのもの。--user
, --password
もマスターのものですが、レプリカ監視用のアカウントを瞬間的に払い出すと楽です。
マスターとレプリカの間でバージョンが違う場合、 --ignore-databases=mysql,sys
は必須です( information_schema
, performance_schema
は pt-table-checksum
が勝手に除けます )pt-table-checksum
のクエリーは個々のテーブルのカラムを全て参照するため、 マスターとレプリカでカラムの数が違ったりマスターにあってレプリカにないテーブルがあると転けます 。
転けるのはpt-table-checksumのみではなく、レプリケーションそのものが止まります 。
--replicate
は「チェックサムの結果を記録するテーブル」を指定します。これを指定しないと、マスターで SELECT
だけ投げて終わるのでレプリカとの比較ができません(更新止まってればできるだろうけど)
--chunk-size
はチャンク分けする時の基本サイズです。特に何も出なければそのまま1000でいいんですが、「チャンク分けが上手くいかずにこのテーブルはスキップするよ!」みたいなワーニングが出ることがあるので、その時はこの --chunk-size
を大きくしてから --tables
でそのテーブルを指定して流しなおします。
--recursion-method
は3306以外のポートを使っている時は hosts
が便利ですが、3306だけなら processlist
で問題ないです。
--no-check-binlog-format
は「レプリカの binlog_format <> STATEMENT
」な時にスクリプトを実行する時に必要なオプションです。
↓を読むと「マスターとレプリカの binlog_format
が違ったらスクリプトを実行させない」のように見えますが、マスターには既に SET SESSION binlog_format = STATEMENT
が実行されているので、レプリカの binlog_format = STATEMENT
でない限り必ず引っ掛かります。
--replicate
で指定したテーブルにFKやTriggerを仕掛けるようなことをしていない限りは(そんなもの好きな人もいないと思う)特に問題ない気がしますが、まあエラーメッセージの通り If you understand the risks, specify --no-check-binlog-format to disable this check.
という感じでお願いします。
--pause-file
を指定しておくと、そのパスに「ファイルが存在している間はスクリプトがsleepする」ようになります。 pt-online-schema-change
と違って実行中ずっと貼りついて見守っていないと不安になるようなスクリプトではないですが、明示的に止めたい時に便利です。 --continue
でもいいんですけどね。
--truncate-replicate-table
を指定すると、 --replicate
で指定したテーブルの中身を1回 TRUNCATE
します。これをやらないと過去の行が残るので、前に pt-table-checksum
を同じコマンドで流した場合に前の結果の残骸が残ることがあります(書き込み自体は REPLACE INTO
なので、過去にも今にも存在するチャンクの情報は上書きされるけれど、過去にあって今ないチャンクの情報が消えない)
--truncate-replicate-table
を指定 しない 場合、前の情報が残っているので --continue
が使えます。
これはCtrl+Cとかで pt-table-checksum
を止めた場合、「その手前までは --replicate
で指定したテーブルにチャンクのチェックサムが残っているはず」ということで、テーブルに載っていないチャンクから処理を再開させることができるオプションです。
なお俺は何度か --truncate-replicate-table
と --continue
を同時に指定して「あれーおかしいなーまた先頭からチェックサム取ってるなー」とかやってたことがあります。我ながらアホだ。
それでは、良いお年を!