こんにちは。クルーズ株式会社CTOの鈴木です。
DBインスタンスのOS/MariaDBのバージョンアップの前段作業として5回目では「DB容量を1.6TB⇒1.1TBに減らした話」をしました。
ちなみにですがDB容量削減という意味ではまだまだ削減の余地はあるものの、プログラム面の改修も大幅に入れないといけないため上記のデータ削減が終わった段階でDBインスタンスのバージョンアップを先行させました。
またRDSの利用についても、まずはDBインスタンスへのリクエストを減らすことが先なので今回のスコープでは見送りDBインスタンスのバージョンアップ後、Cahceの利用などによりDBへのリクエストを減らしたのちの実施としました。
DB切り替えの大枠の流れ
以下の流れで切り替えを検討しました。
事前作業
⓵リプレイス用のインスタンス(AmazonLinux2/MariaDB10.5.5)を切り替え後必要な台数分用意しておく
②既存インスタンスの特定時点のDBバックアップ(dump)をリプレイス用インスタンスにRestoreする。
③既存インスタンスのSlaveのうち、サービスから参照されていないインスタンスをレプリケーション元として、リプレイス用のインスタンスのうちMasterとして使用するインスタンスに対しレプリケーションする。
④リプレイス用のインスタンスのうちMasterとして使用するインスタンスから、残りのリプレイス用のDBインスタンスに対しレプリケーションを行う
整理すると、
既存(Slave)⇒リプレイス用(Slave兼Master)⇒リプレイス用(Slave)
という状況を事前に設定しておき、レプリケーションが追い付くのを待つ
メンテナンス時の作業
⑤既存インスタンスのSlaveのうち、サービスから参照されていないインスタンスをレプリケーション元として、リプレイス用のインスタンスのうちMasterとして使用するインスタンスに対して設定されているレプリケーションを止める。
⑥既存Web、バッチインスタンスのHAProxyに設定されているDBの向き先を既存DBインスタンスのMaster/Slaveから、リプレイス用のインスタンスのMaster/Slaveに切り替える。
挙動に問題がないことを確認後メンテナンスを終了して切り替え完了。
進める中で発生した問題
実際に進めている中でいくつか想定していない問題が発生しました。
DBの設定パラメータが、既存インスタンスのSlave間で値が異なる
これは想定外でした。当社だとSlaveとなっているDBに大きく2つあり、サービスから参照されているSlaveと、管理画面やバッチサーバから参照されているSlaveサーバ(社内ではAdminDBと呼んでいるもの)で設定パラメータが異なっていました。
確かに発行されるQueryの特性はサービスで使うSlaveと管理画面やバッチサーバから発行されるものとは全く異なるので設定パラメータが異なること自体は適切なのですが、それが分かる形でナレッジ化されていなかったためこのような問題が発生したのだと思います。
DBインスタンス切り替え後に発生した問題
2020年10月の2週目に第3回目となるメンテナンスを行い、DBインスタンスを切り替えました。切り替え自体は非常にうまくいったのですが、その後いくつか問題が発生していていました。
旧DBインスタンスをプログラムが参照している箇所があった。
Webインスタンス(PHP)⇒Webインスタンス(HAProxy)⇒DBインスタンス
という構成となっていてWeb インスタンス(PHP)上だとMasterDBへの接続の場合はlocalhost:xxxxx、SlaveDBへの接続の場合はlocalhost:yyyyy(生きている複数のSlaveDBインスタンスに対してラウンドロビン)という状況になっていて、直接DBインスタンスのIPアドレス:3306で接続する想定はなかったが、一部バッチサーバ内のPHPプログラムが直接DBインスタンスのIPアドレスを指定して移行前の旧DBのSlaveインスタンスを参照していた。しかもバッチ処理内でDBの接続文字列が上書きされていた。
実はこの問題が発覚したのは、移行後2週間以上経過したタイミングで、移行前の旧DBインスタンスはサービス上で問題が発生した際に即時切り戻せるようにMariaDBが起動している状況だったためバッチとしては処理が正常に動作しているものの正しく動作していないという状況になっていて、問題が発覚したのは旧DBインスタンスを停止した際にバッチが失敗したためでした。
なので今後システムリプレイスなどでDB移行作業を計画されている方は手間でも移行前に現環境のDBインスタンスのIPアドレスをリスト化しソース上grepかけたほうが良いです。見つからなければ取り越し苦労、見つけたらラッキーです。
バッチの実行速度が最大で2倍遅くなるものがあった。
これは理由がすぐに分かり、DBのSlaveインスタンスがMulti-AZ配置になったためバッチインスタンスが置かれているZoneと参照しているDBインスタンスが異なる場合に今までよりもレイテンシが下がるケースがあるためでした。
対策としてはバッチインスタンスが参照するDBについてはHAProxy上でゾーン毎に接続定義を分け、バッチからは同じZone内のDBSlaveインスタンスを参照するように変更しました。
バッチの処理中でクエリ処理が詰まる問題
調べたところバッチ処理内で CREATE TEMPORARY TABLEを実行してるケースでGAP Lockが発生していで、呼び出し方がCREATE TEMPORARY TABLE XXX(SELECT …);の形で、ネットで文献を調べたとこあまりよろしくない実装っぽく、そのあたりが関係しているのではないか思っています。
バッチプログラムが発行しているSQLをCREATEとINSERT INTO SELECTに書き直した方がよいということは文献よりわかったもののそれが原因と断定できず追加で調査したところ、MariaDBのナレッジベースに以下の記載がありました。
自社の設定を含め、要点を要約すると
・MariaDbのデフォルトのトランザクションの分離レベルは「REPEATABLE READ」である。
・MariaDB10.4までは「innodb_locks_unsafe_for_binlog」という変数があり、この変数がONの場合、GAPロックの発生を防げる。
・移行前のDBインスタンス上のMariaDBのバージョンは10.0で「innodb_locks_unsafe_for_binlog」の設定はONなのでGAPロックは発生していない。
・MariaDB10.4以降では「innodb_locks_unsafe_for_binlog」という変数がなくなってしまっているため、GAPロックの回避には分離レベルを「READ COMMITTED」にする必要がある。
そのことでした。
実際に検証環境でトランザクションの分離レベルを下げて動作確認したところGAPロックの発生はなく、かつ処理的にも分離レベルを下げても問題がないことの確認が取れたため、バッチサーバが参照するDBインスタンスのMariaDBについてはトランザクションの分離レベルを「READ COMMITTED」に変更してこの問題については回避しました。
バッチが参照するMariaDBがメモリ不足で再起動する問題
これはMariaDBバージョン差異の影響やEC2インスタンスを第4世代から第5世代にあげたことによってDBが受け付けられるコネクション量が増えるなど、処理性能が変わったことが影響していると考えられ、「innodb_buffer_pool_size」を設定値より下げ、その後経過観察をしています。
バッチが参照するMariaDBの接続がタイムアウトする処理がある問題
移行前の旧DBを確認したところ、設定ファイル上のタイムアウトのパラメータ値とSHOW VARIABLES コマンドで直接取得したタイムアウトの値が異なっており、過去に誰かが直接書き換えてそのままになっていたため、設定ファイル値にタイムアウトの時間が更新されておらず、その設定ファイルに基づいて新環境のMariaDBのパラメータが設定されていることが原因でした。
まあ、これは緊急対応だったら私も普通にやると思いますが、変更を設定ファイルに書き戻し、どこかの再起動のタイミングで設定ファイルの値を正とした状態に戻すことをしないとこの問題が発生し続けるので構成管理上まずいなと感じました。なので今後は定期メンテのタイミングなど、定期的に今後設定ファイルとコマンドで取得した値の比較を行って、違ったら設定ファイルを更新してDB再起動する運用とします。
取り急ぎ、バッチインスタンスの参照するMariaDBのタイムアウト値を変更しこの問題は回避しています。
まとめ
今回DBインスタンスのOS/MariaDB自体のバージョンアップ作業についてはそこまでトラブルはなかったものの、DBを呼び出すプログラム側の問題(DB接続情報の上書きや非推奨なインサート処理)や、主にバッチから発行されるSQLに起因するDBの挙動に関する切り替え後の問題が多く、移行を行う際にバッチから実行されるクエリのパフォーマンスについて、事前にもう少し調査する必要があるというのが反省点です。
バージョンアップ影響でインスタンスやDB自体のパフォーマンス変わることはある程度やむをえないもので、今回のようにいかに早くチューニングを行い正常化させる進め方で間違ってはいないと考えています。
最後に、今回のDBインスタンスのOS/MariaDBのバージョンアップに際し、事前のデータ消込作業から新インスタンス切替のトラブルシュートまでを2020年の9月・10月の2カ月間、約4.5時間しか時間の取れない深夜メンテナンス×3回で行い、バージョンアップ後に様々な問題も出ましたが、大きなユーザ影響なく短期間で収束できたのは結構すごいことだと思います。
一応予備日として10月4週にもう一日メンテ日程を用意していたのですが、予備日を使うことなくかつ10月30日から開始のMEGA SALEのスケジュールにも影響を出さず無事に完了できました。
今回これだけの仕事をこの短期間で実現できたのは、当社の技術統括部やSHOPLISTの各開発部のエンジニアのスキルの高さ、特に問題にぶち当たった際のトラブルシュート力あってこそで本当に助かりました。
次のアクションとしてはWeb/バッチサーバのOS/インスタンスのバージョンアップ作業で、引き続き実施していきます。
-------------------------------------------------------------------------------------------------
※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした脆弱性リスクへの対処は完了しております。