blog

DeNAのエンジニアが考えていることや、担当しているサービスについて情報発信しています

2024.06.27 技術記事

Google Cloud DMS 利用時のダウンタイムの検証 [DeNA インフラ SRE]

by Masahiro Okazaki

#infra-quality #infra-delivery #stabilization #technical-verification #portability #mysql #dms

はじめに

こんにちは。IT 基盤部の岡崎です。

現在 IT 基盤部では、社内利用が多いクラウドサービスについて、ポータビリティの向上を目指す施策を行っています。 具体的には、異なるクラウドベンダ間で同等の機能を持つサービスへ移行する方法を整えることにより、ベンダロックインされない運用を目指しています。

この施策の一環として、いくつかのクラウドサービスを対象に調査を行いました。調査結果は連載形式で順次公開していく予定です。 すでに以下の記事を公開していますので、こちらもぜひ御覧ください!

本記事では、データベースのポータビリティ向上を対象とし、他環境から Google Cloud へデータベースを移行するためのマネージドサービスである Database Migration Service (以下、DMS。また、単に DMS と記載する場合は Google Cloud の Database Migration Service を指します。)を検証した結果を紹介します。

背景

Web サービスにおいてデータベースは重要なコンポーネントの一つです。 データベースに何らかの問題が発生すると、サービス全体の問題に直結しやすいと言えます。

何らかの理由によりデータベースを移行しなければならないとき、以下のような困難さがあります。

  • データ量が多い場合、移行に時間がかかる
  • 移行前と移行後のデータベースで整合性を保つ必要がある
  • データベースを停止するとサービス自体の停止に直結するため、サービスへの影響を小さくするためにはダウンタイムを短くする必要がある
    • データベースの再起動や切り替えなどによってデータベースへの書き込み・読み込みに影響が発生し、データベースが利用できない時間のことをダウンタイムとする

そこで、今回はクラウドサービスをまたいでデータベースを移行する際の

  • 移行手段
  • その移行手段を利用したときのダウンタイム

に関する調査を行いました。 今回は具体的に特定のサービスを移行したわけではなく、将来的な移行の可能性を踏まえた調査・検証です。

前提条件

DeNA 社内で利用されているクラウドサービスは、AWS と Google Cloud が多数を占めています。 この 2 つのクラウドサービス間でのポータビリティを調査することとし、今回はその中でも AWS から Google Cloud への移行について調査しました。

移行手段

あるデータベースを移行する場合、データのダンプや復元・レプリケーションなど一連の作業を全て手動で行うパターンと、マネージドサービスを利用するパターンが考えられます。 DeNA 社内には専任のインフラエンジニアが存在するチームもあれば、少数のエンジニアが複数の領域を担当しておりインフラ周りに大きな工数をかけられないサービスもあります。 作業を手動で行う場合、細かなニーズに対応できたり、ダウンタイム無しでの移行も検討可能ですが簡単な作業ではありません。 今回はインフラエンジニアがいない環境でも、より低リスクでデータベース移行を実現できるように、マネージドサービスである DMS を利用した移行について調査を行いました。

Database Migration Service とは

オンプレミスや他のクラウドから Google Cloud へ、また Google Cloud 同士のデータベース移行を行うことができるサービスです。 https://cloud.google.com/database-migration

DMS としては異種間のデータベースエンジンの変換を伴う移行(他環境の Oracle を PostgreSQL に変換して移行など)もサポートしています。 ですが今回は、AWS 環境の MySQL データベースを Google Cloud 環境にそのまま移行するパターンについて検証を行います。

利用にあたっての懸念点

データベースの移行の過程でダウンタイムが起きる可能性のあるタイミングとして、以下が考えられます。

  • 既存データベースからデータダンプを実行するとき
  • アプリケーションが参照するデータベースを移行後のデータベースに切り替えるとき

今回はデータの移行部分に着目し、既存データベースからデータダンプを実行するときのダウンタイムについて調査します。

DMS の公式ドキュメントからこれらの情報を確認します。 まずは、DMS を利用した AWS からの MySQL のデータ移行についての公式ドキュメントを確認します。

これらを見ると、移行元のデータベースへの書き込みを約 20 秒停止する必要があると書かれています。

また、DMS を利用して MySQL のデータ移行を行う際の既知の制限の中に、データダンプを並列処理で実行する際の考慮事項が記載されています。 (参照 : https://cloud.google.com/database-migration/docs/mysql/known-limitations#parallelism-considerations) ここではデータベース内のテーブル数に応じてデータベースのロック時間が変化することが記載されています。

このように、複数の情報が存在し実際にどの程度のダウンタイムが発生しうるか不明瞭な状態です。 サービスの要件に応じて許容できるダウンタイムの長さは変わりうるため、各チームがこのツールが利用できるか判断できるようにするためにも、具体的なダウンタイムの長さとその影響は把握しておきたいです。

そこで、以下の二点から DMS の利用にあたって懸念があるかどうかを検証します。

  1. データの取得の際、ダウンタイム発生につながるクエリが実行されるかどうか
  2. データ取得に伴うダウンタイムの有無とその程度

ダウンタイムの検証

前提

今回は以下の条件で検証を行います。

  • 移行元データベース : Amazon Aurora MySQL
  • 移行先データベース : Cloud SQL for MySQL
  • データベースエンジン : MySQL 8.0

検証環境の準備

移行元データベース作成

移行元のデータベースを Aurora にて作成します(具体的な作成方法については省略します)。 インスタンスは以下の条件で作成します。

  • インスタンスサイズ : db.r6g.large
  • クラスター内にインスタンスは 1 台

DMS の設定

基本的な DMS の利用方法についてはここでは省略します。 公式ドキュメントが提供されていますので、そちらを御覧ください。 https://cloud.google.com/database-migration/docs/mysql/quickstart

ここでは、DMS を利用するにあたって設定したパラメータを列挙します。 今回の調査は細かくパラメータを調整することが目的ではなく、より簡便にデータベースの移行を実現するための検証であるため、できるだけ DMS のデフォルト値をそのまま利用しています。

  • 移行元データベースエンジン : Amazon Aurora MySQL
  • 移行ジョブの種類 : 「継続的」と「1 回限り」をそれぞれ検証
  • 初期読み込み構成のカスタマイズ
    • 初期読み込みファイルの生成方法の選択 : 初期読み込みを自動生成する
      • 送信元データベースのダンプファイルの作成方法を指定します。自動生成するを選択した場合、DMS によって自動的にダンプが取得されます。また、別途取得したダンプファイルを Google Cloud Storage(以下、GCS)に配置し、それを参照することもできます。
    • 初期読み込みオペレーションの構成 : 初期読み込みの並列処理(推奨)
      • 送信元データベースのダンプを実行する際、並列処理を行います(MySQL 5.7 / 8 のときのみ利用可能)
    • 並列処理のレベル : Optimal
      • 送信元データベースへの負荷のバランスを取るレベルです
    • 下記ドキュメントを参照

検証 1 : 実行クエリの確認

最初の検証として、DMS から Aurora に対して具体的にどのようなクエリが実行されているかを確認します。 実際に発行されているクエリの内容から、移行の際にどのような影響が考えられるかを確認します。

この検証では、移行ジョブの種類として選択できる「継続的」と「1 回限り」のそれぞれでクエリの確認を行います。

確認方法

DMS から Aurora に対して実行されるクエリを確認するために、Aurora 側の一般クエリログを有効化します。 有効化するためには、DB パラメータグループでパラメータを設定する必要があります。 具体的な設定方法については以下のドキュメントを参照してください。 https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/USER_LogAccess.MySQL.LogFileSize.html#USER_LogAccess.MySQL.Generallog

記録されたクエリログについては、マネジメントコンソールから参照可能です。

継続的の場合

移行ジョブの種類として「継続的」を選択した場合、ダウンタイムに影響する内容としてテーブルロックが実行されていました。 テーブルロックに関連するクエリとして、以下のようなクエリが実行されていました。

  • FLUSH TABLES WITH READ LOCK
  • LOCK TABLES … READ
  • START TRANSACTION WITH CONSISTENT SNAPSHOT

これらのクエリについて内容を確認します。

FLUSH TABLES WITH READ LOCK

FLUSH TABLES WITH READ LOCK は開かれている全てのテーブルを閉じ、グローバルな読み取りロックを保持している全てのデータベースの全てのテーブルをロックします。(参照: https://dev.mysql.com/doc/refman/8.0/ja/flush.html

このクエリの実行には SUPER 権限が必要ですが、Aurora の利用にあたって SUPER 権限は利用できないため、このクエリは失敗します。(参照: https://repost.aws/knowledge-center/mysqldump-error-rds-mysql-mariadb

LOCK TABLES … READ

LOCK TABLES … READ はテーブルごとに読み取りロックを取得します。(参照: https://dev.mysql.com/doc/refman/8.0/ja/lock-tables.html

これにより、ロックを保持しているセッション以外からの更新ができなくなります。ロックを保持しているセッションが明示的に UNLOCK TABLES を実行する、もしくはトランザクションを実行することでテーブルのロックが開放されます。

START TRANSACTION WITH CONSISTENT SNAPSHOT

START TRANSACTION WITH CONSISTENT SNAPSHOT は一貫性のある読み取りを行い、ある時点でのデータベースのスナップショットを取得します。(参照 : https://dev.mysql.com/doc/refman/8.0/ja/commit.html, https://dev.mysql.com/doc/refman/8.0/ja/innodb-consistent-read.html

トランザクションが実行されるため、このクエリが実行された時点でロックが開放され、他のセッションからの書き込みが許可されます。 すなわち、LOCK TABLES … READ が実行されてから START TRANSACTION WITH CONSISTENT SNAPSHOT が実行されるまでの間がテーブルロックの時間となります。

DMS で利用されているダンプ手法について

テーブルロック解除後、実際のデータを参照しているクエリを確認すると、以下のようなクエリが実行されていました。

Query	SELECT SQL_NO_CACHE `id`,`k`,`c`,`pad` FROM `sbtest1`.`sbtest7` WHERE (`id` BETWEEN 1 AND 10) ORDER BY `id` /* mysqlsh dumpInstance, dumping table `sbtest1`.`sbtest7`, ID: chunk 0 */

これより、DMS で初期読み込みの並列処理を実行すると、裏では MySQL Shell の dumpInstance が実行されていることがわかります。 また MySQL Shell のダンプに関連するドキュメントを確認すると、上記に挙げたクエリに関する説明が存在します。 https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-shell-utilities-dump-instance-schema.html

継続的の場合のまとめ

初期読み込みの並列処理を選択したうえで継続的な移行を実行すると、裏では MySQL Shell の dumpInstance が実行されていることがわかりました。 このとき、一貫性のある読み取りを行うため一時的にテーブルロックを取得します。 このテーブルロックにかかる時間が、DMS の実行の際の移行元データベースに対する書き込みのダウンタイムに影響します。 また、影響を受けるのは書き込みだけであり、読み込みには影響を受けないと言えます。

一回限りの場合

移行ジョブの種類として「一回限り」を実行した場合、継続的の場合と同様、ダンプには MySQL Shell が利用されているようでした。 しかし、継続的な移行を実行したときに記録されていたようなテーブルロックに関するクエリは記録されていませんでした。 また実際に DMS を用いた移行実施中にダウンタイムの有無を計測したところ、ダウンタイムの発生なく移行が完了しました。 ダウンタイムがなく移行が完了することは喜ばしいことですが、テーブルロックが実行されていないということは、一貫性の無いバックアップが取得される可能性があるということになります。

テーブルロックが実行されず明確な静止点が確保されない場合、読み取りのクエリが実行された時点でのデータが移行先に転送されます。 移行途中に未転送のレコードと転送済みのレコードそれぞれに影響する書き込みが行われた場合、転送済みのレコードに関しては変更が反映されず、未転送のレコードに関して変更が反映された状態でデータの移行が行われます。 このように、DMS 実行中に移行元データベースに対してデータの書き込みが実行されている場合、移行先データベースに移行されたデータの整合性が担保されていない可能性があります。

このことから、一回限りの移行を行う場合 DMS の実行中は移行元に対する書き込みリクエストを完全に停止したうえでの実行が必要です。 実際にサービスで利用されているデータベースの場合、データを移行する間ずっと書き込みを停止させられるユースケースは限られます。 一回限りの移行を利用する場合、Aurora であればクローン等を利用して読み書きの発生しない状態のインスタンスを事前に作成し、そのインスタンスに対して処理を実行するなどの対応を取る必要があります。

検証 2 : ダウンタイム時間の計測

次に、実際のダウンタイム時間の計測を行います。 検証 1 よりダウンタイムに影響する内容としてテーブルロックが取得されることがわかりました。 ここではテーブルロックの長さを測定し、その長さがどのようなパラメータに影響を受けるのか確認します。

確認内容・方法

まず最初に、以下のドキュメントに示されているようにテーブル数の大小がテーブルロックの長さに影響を与えるかについて確認を行います。 https://cloud.google.com/database-migration/docs/mysql/known-limitations#parallelism-considerations

移行するデータと任意の数のテーブルを作成するために sysbench を利用します。 テーブルサイズは 10 とし、テーブル数は 10, 1000, 10000 の 3 パターンで検証を行います。

sysbench \
--mysql-host=AURORA_ENDPOINT \
--mysql-port=3306 \
--mysql-db=DB_NAME \
--mysql-user=DB_USER_NAME \
--mysql-password=DB_USER_PASSWORD \
--tables=TABLE_NUMBER \
--table-size=10 \
oltp_common prepare

次に、データの移行中にテーブルの読み書きが発生している場合、それらがダウンタイムの長さに影響を与えるかについて確認します。 テーブル数は 10000 とし、読み書きが発生している場合としていない場合でのテーブルロックの時間を比較します。 読み書きは、sysbench の負荷テストを利用して発生させます。 テスト時間を DMS の処理時間より十二分に長く設定し、sysbench によって負荷がかかっている間に DMS を実行します。

実行方法として、ローカルの PC から移行元の Aurora に対して sysbench を実行します。 tablesthreads については、負荷をかけるためにできるだけ大きな値を設定します(大きすぎる値を設定すると sysbench の実行に失敗するので、適宜調整します)。

sysbench \
--mysql-host=AURORA_ENDPOINT \
--mysql-port=3306 \
--mysql-db=DB_NAME \
--mysql-user=DB_USER_NAME \
--mysql-password=DB_USER_PASSWORD \
--tables=200 \
--threads=8 \
--table-size=10 \
--time=TIME \
oltp_read_write run

検証 1 の内容を踏まえ、継続的な移行のみを検証します。 テーブルロックの時間は、最初に LOCK TABLES … READ が実行されてから、最後に START TRANSACTION WITH CONSISTENT SNAPSHOT が実行されるまでの時間とします。

テーブル数による検証

以下にテーブル数と、そのときのテーブルロックの時間を示します。 ただし、Aurora の利用に伴ってデフォルトで作成されるテーブルはこれらとは別に存在し、同時に DMS によって移行されます。

テーブル数 テーブルロックの時間(s)
100 0.1
1000 0.4
10000 12

上記より、ドキュメントの通りテーブル数が多くなることによってテーブルロックの時間が長くなることがわかりました。 また、10,000 テーブルであっても 12 秒程度のロック時間であるため、サービスへのアクセスが少ない時間帯に作業を行うという選択肢も検討できると考えられます(もちろん、サービスの特性や条件により異なります)。

書き込み負荷による検証

以下に負荷無し・ありそれぞれのときのテーブルロックの時間を示します。 負荷無しのデータについては、テーブル数による検証のものです。

負荷無しのときのテーブルロックの時間(s) 負荷ありのときのテーブルロックの時間(s)
12 11

上記より、負荷がかかっている状態でもロック時間に大きな影響を与えないことがわかりました。

また、ドキュメントからは DMS を実行する際には移行元のデータベースにおいて約 20 秒書き込みが停止している必要があり、DMS は書き込みが停止していることを認識して処理を進めると記載されています。 (参照 : https://cloud.google.com/database-migration/docs/mysql/aurora-no-superuser)

ただし sysbench により負荷をかけながら処理を実行したところ、負荷がかかっている間も DMS の処理自体は継続していました。 処理の中でテーブルがロックされるため一時的に書き込みはできなくなりますが、DMS の処理としては停止することなく実行されました。

まとめ

本記事では AWS から Google Cloud へ MySQL データベースの移行を行うことを想定し、Google Cloud の DMS というマネージドサービスについて検証しました。

特に移行中のダウンタイムに注目し、DMS で実行される詳細なクエリを調査しました。 その結果、初期読み込みの並列処理を利用した場合、テーブルロックによる書き込み処理のダウンタイムが発生することがわかりました。 また、このテーブルロックの時間は移行対象のテーブル数に比例することもわかりました。 実際にデータベースの移行においてこのテーブルロックの時間を許容できるかどうかは各サービスの要件によりますが、多くの場合ダンプの実施タイミングの調整等によって許容できるレベルだと思われます。

DMS 自体はドキュメントも豊富で使い勝手もよく、細かなチューニング無しでもデータベースの移行が可能でした。 今回検証したダウンタイムに留意することで、より安全に移行を実現できるといえます。 今後 Google Cloud へデータベースを移行する際には積極的に利用を検討していきたいと思います。

最後まで読んでいただき、ありがとうございます!
この記事をシェアしていただける方はこちらからお願いします。

recruit

DeNAでは、失敗を恐れず常に挑戦し続けるエンジニアを募集しています。