RDB間の同期 ーDMS Serverlessを用いた手法
はじめに
記載事項に関して、誤りやこうした方が良いという点があれば、コメントしてくださると非常にありがたいです。よろしくお願いいたします。
実現したいこと
- AWS上にある本番RDS(MySQL)と開発RDS(MySQL)間の同期を任意のタイミングで行いたい(継続的なものではなく、その時点の本番DBスナップショットを開発DBで復元するイメージ)。
- 本番DBのダウンタイムはできるだけ短く。
- 時間、費用がかかるので、全てのテーブルで同期を取る必要はなく、ある特定のprefixが付いたテーブルのみを同期したい。
- AWS CLIなどでコンソールを用いず、簡単に実行できるようにする。
考えた事
案1:本番DBでスナップショットを取得し、開発DBで復元する
一番簡単で手っ取り早く、AWS CLIでも実行できそう
しかし、この開発DBはコスト削減のため、本番DBよりもストレージサイズを小さくしていた。
RDSの仕様でストレージサイズが異なる場合、スナップショットからの復元ができないらしい。
故に却下。
案2:mysqldumpを使用したデータ移行
従来から使われてきた方法で、安心感あり。
しかし、時間がかかる&ローカルにダウンロードする際にアウトバウンド通信費用が発生する。
またサーバに負荷がかかるため、実行タイミングに気をつかわなければならない。
故に却下。
案3:AWS DMSによるデータ移行
AWSで提供されているデータ移行ツールで、簡単な設定でDB間のデータ移行を実現できる。
ドキュメントには
お客様のデータベースの AWS への移行を事実上ダウンタイムなしで行えます。
との記載あり。料金も移行中に使用したコンピューティングリソースとログのストレージにのみ課金されるので、設定さえきちんと行えば安心そう。
上記の事情から案3:AWS DMSによるデータ移行を検証することにしました。
さらに調べてみると、DMSにはDMSサーバレスなるものがあり、これはデータ移行が終わるとそれに使用したリソースをサービス側で勝手に解除してくれるらしい!
なるべくコストを安く抑えたいので、DMSサーバレスで検証を行うことにしました。
DMSサーバレスの設定
実際にDMSサーバレスの設定を行っていきます。
設定手順は大きく分けて以下の3つです。
- セキュリティグループ、サブネットグループなどネットワーク関連の設定
- ソース・ターゲットエンドポイントの登設定
- レプリケーションの設定
※DMSはソースの変更をキャプチャして、レプリケーションすることもできますが、今回の目的には合わないので、1回限りのレプリケーション(フルロード)機能を用いて、バッチの手動実行的な使い方をしています。
こちら注意点に絞って、説明したいと思います。
※こちらのドキュメントを参考に設定を行いました。
注意点1:ワイルドカードの記述方法が特殊
これは上記手順3で、レプリケーション対象のテーブルを設定する際に引っかかりました。
今回はRDS(MySQL)→RDS(MySQL)でしたので、例えば「asia_」というprefixがつくテーブルを移行したいときは、MySQLの記法に倣って以下のように移行対象テーブルを記述しました。
asia\_%
しかし実際に実行してみると、アンダーバー_
がワイルドカードとして読み込まれてしまって、\
でエスケープできていませんでした。
よくよくドキュメントを見てみると以下表のような記載があり、正しくは以下のように記載すべきでした。
asia[_]%
MySQLでのワイルドカードの記述方法に慣れている方は注意が必要です。
ワイルドカード | マッチ |
---|---|
% | Zero or more characters |
_ | A single character |
[_] | A literal underscore character |
[ab] | A set of characters. For example, [ab] matches either 'a' or 'b'. |
[a-d] | A range of characters. For example,[a-d] matches either 'a', 'b', 'c', or 'd'. |
注意点2:ターゲットテーブル準備モードの設定
これも設定手順3の内容で、レプリケーションの際にテーブルの移行方法を定めるものです。
もっと詳しく言うと、ターゲットエンドポイント上に移行対象テーブルと同名のテーブルがあった場合、どのように処理するかを定義します。
移行方法は以下の3つから設定することができますが、モードの選択を誤ると正常にデータ移行ができない可能性があるので、注意が必要です。
何もしない
「何もしない」モードは、ターゲットエンドポイント上に移行対象テーブルと同名のテーブルがあった場合、それらを変更しません。それ以外の場合は、新しくテーブルを作成します。
ターゲット上のテーブルを削除
「ターゲット上のテーブルを削除」モードは、ターゲットエンドポイント上に移行対象テーブルと同名のテーブルがあった場合、それらは削除し、ソースDのテーブル構造を参考に、新しいテーブルを作成します。ただしメタデータは移行されない(はず)。
切り捨て
「切り捨て」モードは、ターゲットエンドポイント上に移行対象テーブルと同名のテーブルがあった場合、テーブルとそのメタデータをそのまま残し、そこからデータを削除します。
ただしソース側とターゲット側でテーブル定義が異なると、正常に移行できないときがある。
今回はソース側のテーブル定義が頻繁に変更される状況であったため、「ターゲット上のテーブルを削除」モードで設定しました。
検証結果
検証方法として、以下を行いました。
・DMSの機能であるデータ検証を有効化
・本番DB、開発DBでレコード数を比較
ここに関して、開発DBに関してそこまでデータの正確性を求められないという事と、時間がなかったことから上記のような検証で済ませてしまいましたが、もっと良い方法がある気がしますので、何かご存じでしたら教えて下さい!
結果としては、レコード数は対象テーブルで差異がなく、移行を完了することができました。
しかしながら、上述したように「ターゲット上のテーブルを削除」モードではメタデータを移行できないという問題があります。
また以下のような新しい問題も発覚しました。
DATE型の「0000-00-00」というデータがNULLとして移行される。
ソースDBのあるカラムは、DATE型でIS_NULLABLE=YESとなっていました。
これをそのまま移行すると、ターゲットDBでのデータがNULLとして移行されてしまいました。
この問題については以下の記事が参考になりましたので、ご覧ください。
この話は少しややこしく、長くなってしまうので、簡単に説明しますと、「0000-00-00」はMySQL以外ではサポートされていないデータであり、その影響で今回DMSからMySQL→DMS→MySQLとデータ移行する中でターゲット先のカラムがNULl許可していたため、NULLに変換されてしまったということです。
この対策としては、以下の3つが考えられました。
- そもそもお客さんがそれで問題ないならいいじゃん!
- ソースDBのDATE型で「0000-00-00」というデータを持つカラムを修正する。
- 移行する前に対象カラムのIS_NULLABLEをNOに変更する。
1についてはそれはそうなのですが、技術的に解決できそう & 時間に余裕もあったので却下。
2について、「0000-00-00」というデータが結構な数あり、これをNULLや有効なデータに変換した際の影響が分からず、洗い出しにも時間がかかりそうということで却下。
消去法で3を検討することとしました。
これについては後ほど別の記事にまとめたいと思います。
Discussion