MySQL道普請便り

第140回 オンラインスキーママイグレーションツール gh-ostを使ってみよう[その3]

この記事を読むのに必要な時間:およそ 3 分

前回,前々回に続いて,オンラインスキーママイグレーションツール gh-ostについて紹介したいと思います。未読の方は,第138回 オンラインスキーママイグレーションツール gh-ostを使ってみよう[その1]第139回 オンラインスキーママイグレーションツール gh-ostを使ってみよう[その2]をご確認いただくことをおすすめします。

その他オプション

ここでは,覚えておくと得するオプションを紹介します。

--chunk-size int

基テーブルの既存データをゴーストテーブルへコピーするときのチャンクサイズを指定します。デフォルトは1000です。

以下は一般ログから出力したものですが,1回のステートメントで1000行の単位でコピーしているのがわかります。

insert /* gh-ost `sysbenchdb`.`sbtest1` */ ignore into `sysbenchdb`.`_sbtest1_gho` (`id`, `k`, `c`, `pad`)
(select `id`, `k`, `c`, `pad` from `sysbenchdb`.`sbtest1` force index (`PRIMARY`)
where (((`id` > _binary'1') or ((`id` = _binary'1'))) and ((`id` < _binary'1000') or ((`id` = _binary'1000')))) lock in share mode)

--allow-on-master

マイグレーションをマスター上で直接実行するようにします。つまり,バイナリログの抽出先をマスターにして,レプリカを使用しません。--hostオプションにマスターのホスト名を指定して実行します。

gh-ost \
--host=Master.Hostname \
--port=3306 \
--user="ghost_user" \
--password="xx" \
--database="sysbenchdb" \
--alter="ALTER TABLE sbtest1 ADD col1 int;" \
--allow-on-master \
--execute

--exact-rowcount と --concurrent-rowcount

--exact-rowcountが指定されると,SELECT COUNT(*)を実施して実際の件数を取得します。これにより,作業完了予定時間が正確になります。デフォルトはFalseなので,統計情報から行数を推測します。--concurrent-rowcount--exact-rowcountと連動しています。

Trueの場合,SELECT COUNT(*)をマイグレーション作業と並列に動作します。デフォルトはTrueです。

--test-on-replica

テストのために使用されるオプションです。マイグレーションをレプリカのみで実施し,終了時にテーブルをカットオーバせずにレプリケーションを停止します。よって,基テーブルとゴーストテーブルが残っているのでデータを比較することが可能です。gh-ostにて正常にマイグレーションできるかテストすることができます。ゴーストテーブルを削除して,レプリケーションを開始することで元の構成に戻すことができます。

ただし注意として,実際にレプリカにデータを書き込むので,gtid_executedの値がマスターとレプリカで異なることになります。HAとしてフェールオーバするような仕組みであれば,正常に切り替えできない恐れがあるので,テスト終了後はマスターと同じgtid_executedに変更してからレプリケーションを開始するのが良いでしょう。

注意点

最後に,gh-ostを使用するときに注意する点を紹介します。

ユニークキーの追加

gh-ostを使用して,ユニークキーを追加することは可能です。しかし,ユニークキーを追加するカラムの値が事前にユニークな値になっていることを確認してから実行しないと,データがなくなってしまう恐れがあります。

以下のように,テーブルとデータを用意して試してみましょう。

mysql> CREATE TABLE `uniq_test` (
  `id` int NOT NULL,
  `uniq` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT * FROM uniq_test;
+----+------+
| id | uniq |
+----+------+
|  1 |    1 |
|  2 |  100 |
|  3 |    1 |
+----+------+
3 rows in set (0.00 sec)

uniqカラムに対してユニークキーを貼ってみたいと思います。id:1とid:3のレコードのuniqカラムの値がどちらも1となっているため,uniqカラムの値はユニークではありません。

まずは,mysqlコマンドで実行するとエラーとなります。

mysql> ALTER TABLE uniq_test ADD UNIQUE KEY (uniq);
ERROR 1062 (23000): Duplicate entry '1' for key 'uniq_test.uniq'

では,gh-ostで実行してみます。

gh-ost \
--host=192.168.0.2 \
--port=3306 \
--user="ghost_user" \
--password="xx" \
--database="sysbenchdb" \
--alter="ALTER TABLE uniq_test ADD UNIQUE KEY (uniq);" \
--execute

<snip>
# Done

ログは割愛しましたが,正常終了となる# Doneが表示されました。

mysql>SELECT * FROM uniq_test;
+----+------+
| id | uniq |
+----+------+
|  1 |    1 |
|  2 |  100 |
+----+------+

データを見てみると,id:3のレコードがなくなっています。

このような動作になるので,ユニークキーを追加する前には,必ずそのカラムの値がユニークになっているか確認してから実行しましょう。

なぜこのようなことが起こるのでしょうか。

簡単に説明しますと,基テーブルからゴーストテーブルへデータをコピーする際に,実装の都合上INSERT IGNORE INTO..を使用しているためです。INSERT IGNORE INTO..の問題点は,エラーが発生してもすべてのエラーを無視することです。それによりユニークキーによる重複エラーも無視され,このような状況が起こります。

マイグレーション後のデータロストを懸念する方は,--test-on-replicaオプションで先にテストすると良いでしょう。

ストレージサイズ

gh-ostはMySQLのストレージを多く使用します。ゴーストテーブルは基テーブルと同等のサイズになりますし,バイナリログも大量に書き込みます。よって,安全にマイグレーションを行うには,基テーブルの2倍以上の空き領域が残っていることを確認してから実行しましょう。

スワップ

gh-ostではMySQLのバイナリログを大量に書き込むと説明しました。それは,行ベースレプリケーションかつbinlog_row_image=FULLを必須としているためです。

行ベースレプリケーションは行単位でバイナリログに書きます。そして,binlog_row_image=FULLであると,一部カラムの更新であっても行の全体のbefore imageとafter imageをバイナリログに書きます。よって,バイナリログに多くの書き込みが発生する事になります。

バイナリログ書き込みの仕組みは,ページキャッシュを介してディスクへsyncする動きになります。そこで起こりうる問題として,マイグレーション中に大きなテーブルだとバイナリログを大量に書き出され,ページキャッシュが枯渇しスワップが発生する恐れがあります。

MySQL 8.0からは,binlog_expire_logs_secondsパラメータによってバイナリログのパージを秒数で指定できるようになりました。大きなテーブルをgh-ostでマイグレーションするときはこのパラメータを調整してバイナリログを早めにパージするのが良いでしょう。これにより、ストレージサイズの問題も併せて解消されます。

スロットル

スロットルにおいても注意点があります。高負荷なMySQLであると,スロットル条件が厳しすぎるといつまでもスロットリング状態となり,マイグレーションが終了しない状況になる可能性があります。適宜ワークロードに合わせたしきい値を設定するといいでしょう。

また,稼働中のgh-ostに対してオンラインでスロットルのしきい値を変更することもできます。詳しくは, Interactive commands をご確認ください。

まとめ

gh-ostについて3回に渡って紹介しました。簡単にまとめると,gh-ostを実際に使う際には,以下の観点に注意して進めるのが良いでしょう。

  1. ディスクサイズなど実行前に注意点に記載してあることを確認する
  2. レプリカの設定がbinlog_format=ROW,binlog_row_image=FULL,log_slave_updates=ONになっていることを確認します。
  3. gh-ostは最小限のオプションでも割と安全に動作します。
  4. スロットルを--max-lag-millis--max-loadオプションで調整する
  5. カットオーバーをハンドリングするには--postpone-cut-over-flag-fileオプションを使用する。
  6. --test-on-replicaオプションで確実にマイグレーションを行うためにテストする

そして,今回説明しきれませんでしたが,Hook機能や,AWSをはじめ各クラウドサービスで実行するためのオプションなど,さまざまな機能があります。一度,document一覧 に目を通してから実際に使ってみるのが良いと思います。

著者プロフィール

北川健太郎(きたがわけんたろう)

LINE株式会社所属のデータベースエンジニア。担当はMySQLとOracle Database。好きなMySQLの機能はレプリケーションで,好きなOracleDatabaseの機能はログオントリガー。

Twitter:@keny_lala