💻

本番稼働していて負荷をかけられないTiDBへの追加移行におけるAWS DMSのチューニングTips集

に公開1件

この記事は レバテック開発部 Advent Calendar 2025 16日目の記事です。

はじめに

レバテック開発部、SREチームに所属している金澤です。

現在、レバテックではTiDBをマルチテナントで利用しています。
元々はAurora MySQLをメインで使用しており、移行にはAWS DMSを採用しました。

今回は、TiDB移行におけるAWS DMSを利用する事例が少なかったこともあり、工夫した点をまとめたいと思います!

↓TiDBに関連する過去の記事
https://zenn.dev/levtech/articles/switch-table-name-on-tidb
https://zenn.dev/levtech/articles/bc0786a0c7c849
https://zenn.dev/levtech/articles/2de2a3294924e4

前提

移行対象

  • ソースデータベースはAurora MySQL
  • ターゲットデータベースはTiDB Dedicated
    • TiDBは本番で利用しているシステムが存在するため、過度な負荷をかけることができない

AWS上で必要な主なリソース

  • ソースエンドポイント
  • ターゲットエンドポイント
  • レプリケーションインスタンス
  • 移行タスク

TiDB特有の観点

移行用のユーザーとリソースグループを作成する

TiDBにはリソースグループと言われる、TiDBクラスター上のリソース配分を分割すること機能があります。
https://docs.pingcap.com/ja/tidb/stable/information-schema-resource-groups/
https://docs.pingcap.com/ja/tidb/stable/tidb-resource-control-ru-groups/

今回は、DMSで利用する移行用のユーザーとリソースグループを作成し、RuPerSec[1]をリソースグループに設定することで、本番稼働中のシステムにデータ移行による負荷の影響を与えないように調整しました。

移行用のNodeGroupを分離する

TiDBクラスター内で、クライアントとの接続を担当するTiDBノードをグルーピングする事が可能な機能が提供されています。
https://docs.pingcap.com/ja/tidbcloud/tidb-node-group-overview/

こちらも、複数の移行タスクを実行するため、本番稼働中のシステムに影響を与えないよう分離しました。

DMSにおける工夫

移行タスク

MaxFullLoadSubTasksを1にする

フルロード時に並列でロードするテーブル数を設定を1にして直列にしました。
前述の通りRUでリソースを絞っているため、並列度を上げると各サブタスク同士でTiDBのリソースを潰し合ってしまい、結果リソース解放待ちでタイムアウトとなりフルロードが失敗するテーブルが多く発生しました。
並列でやるだけやって失敗したテーブルは再実行する、という選択肢もありますが、時間をかけても確実に一度で完了させたかったため、今回は直列でロードを行いました。

移行タスクの分割

移行作業開始当初は1つの移行タスクで全てのテーブルをロードしていました。
しかし全体のロード時間の短縮、設定や接続ユーザーの分割のために以下のように移行タスクを分割しました。

  • マスターテーブル
  • タイムスタンプ保持テーブル
    • タイムスタンプのズレが発生した時に切り戻ししやすいように
  • LOBデータ保持テーブル
    • FullLOBモードになると全体のレプリケーションで遅延が発生するため
  • レコード数が多いテーブル
    • フルロード中に他のテーブルのCDC(Change Data Capture)に影響を与えないように、さらに別のユーザーとリソースグループを作成して利用する
  • その他全て

レプリケーションインスタンスは、各タスクで共通の1台を利用しました。

LOBのフルロード時はインラインLOBモードを有効化する

FullLOBモードは、各レコード毎に、LOB以外のデータをインサート→対象のレコードを検索→LOBデータを注入するという流れを取るため、通常データのロードと比較してパフォーマンスが悪化します。

インラインLOBモードは、通常のデータのインサート時にLOBデータも含めることができるようする設定です。LOBデータのInlineLobMaxSizeを指定し、上限以上のデータはFullLOBモードでロードします。

効果が大きかったテーブルでは、FullLOBモードで10時間かかっていたロードが、インラインLOBモードで1時間弱に改善できました!

ただし、早くなるからと言ってInlineLobMaxSizeを大きくしすぎると、インスタンスのメモリ消費が大きくなります。
実際のデータサイズに基づいて適切な値を設定することをおすすめします。

テーブル内で並列度を上げる

1テーブル内のレコード数が多い場合はテーブル内での並列度を上げる(parallel-load)ことでロード時間の短縮を実現しました。
分割はテーブルのIDベースで行いました。

Terraformによる設定例
{
  "rules" : [
    {
      "rule-type" : "selection",
      "rule-id" : "1",
      "rule-name" : "1",
      "object-locator" : {
        "schema-name" : "hoge_schema",
        "table-name" : "fuga-table"
      },
      "rule-action" : "include"
    },
    {
        "rule-type": "table-settings",
        "rule-id": "2",
        "rule-name": "2",
        "object-locator": {
            "schema-name": "hoge_schema",
            "table-name": "fuga-table"
        },
        "parallel-load": {
            "type": "ranges",
            "columns": [
                "id"
            ],
            "boundaries": [
                [
                    "20000000",
                ],
                [
                    "40000000"
                ],
                [
                    "60000000"
                ],
                [
                    "80000000"
                ],
                [
                    "100000000"
                ]
            ]
        }
    }
  ]
}

フルロードとCDCの間で一度移行タスクを停止する設定を入れる

DMSの仕様では以下のようにフルロードからCDCへと切り替わります

  1. フルロードを実行
  2. フルロード開始以降の変更分はレプリケーションインスタンス内でキャッシュする
  3. フルロード完了
  4. フルロード開始以降の変更分を反映
  5. CDC開始

移行タスクには、4と5の反映前に移行タスクを一度停止する設定があります。
我々は今回利用しませんでしたが、フルロードとCDCそれぞれのフェーズで最大限にパフォーマンスを改善したい際は、以下の設定が有効な手段となります。

  • StopTaskCachedChangesApplied
    • trueにすると4が完了後に一度移行タスクが停止
    • フルロード完了後に外部キー制約を有効化したい場合などに利用
  • StopTaskCachedChangesNotApplied
    • trueにすると3が完了後に一度移行タスクが停止
    • フルロード時のチューニングでPKやインデックスを削除していた場合
      • プライマリキーやセカンダリインデックスを追加することで、CDCの反映時に更新対象のデータ参照のパフォーマンス改善しCDCの高速化が実現可能

レプリケーションインスタンス

レプリケーションインスタンスのauto_minor_version_upgradeはオフ

実は以前、レコード数が膨大でロードに時間のかかるテーブルのフルロード完了直前に、インスタンスの自動アップグレードが実行されてしまい、最初からやり直しになったことがありました…。

基本的な設定かもしれませんが、同じ思いをする方が一人でも減るように、自戒を込めて共有させていただきます。

CDCによるデータ連携が止まっているときはインスタンスのストレージを確認する

DMSではフルロード完了後、CDCによる連携をそのまま開始することができます。
その際にレプリケーションインスタンスのストレージが枯渇すると、CDCが一時的に停止され、ラグが大きくなります。
(※一定時間後解放されて再開される)

レプリケーションタスクの、CDCLatencySourceとCDCLatencyTargetの値が大きいときは、インスタンスの空きストレージを確認してみてください。

また、ストレージサイズの変更を伴うインスタンスの再起動は、長いと1時間ほどかかるので注意が必要です。

エンドポイント

タイムスタンプの値がずれる

レバテックで利用するデータベースはAurora、TiDBともにJSTを利用しています。
DMSを経由するとタイムスタンプが9時間前後する問題が発生し、今まで以下のような対応をしてきました。

  • ソースエンドポイント、ターゲットエンドポイント双方の接続設定でタイムゾーンをUTCにする
  • ソースエンドポイント、ターゲットエンドポイント双方の接続設定でタイムゾーンをUTCにし、文字列としてレプリケーション
  • ソースエンドポイントはJST、ターゲットエンドポイントはUTCで接続

移行作業の度に挙動が異なるケースがあり、根本原因の特定は継続課題となっています。
直近では3つ目の構成を採用することで解消できました。

MaxFileSizeを下げる

フルロード時にターゲットに書き込むために生成されるCSVファイルのサイズ上限値で、ターゲットエンドポイントに設定します。

https://docs.aws.amazon.com/ja_jp/dms/latest/userguide/CHAP_Target.MySQL.html

一度の書き込み量が多いと、TiDB側でタイムアウトするためデフォルトの32MB→8MB(8192KB)に変更しました。

おわりに

DMSは設定項目が多い分、適切に利用するハードルが非常に高く感じました。
一方で柔軟性が非常に高く、しっかりとDeepDiveすれば、汎用的に利用できる素晴らしいツールであると改めて実感しました。

他の観点もあれば、是非コメント等でアドバイス頂けたらと思います!

参考リンク

https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2025_AWS-Database-Migration-Service_0731_v1.pdf
https://docs.aws.amazon.com/ja_jp/dms/latest/userguide/Welcome.html

脚注
  1. RUはTiDBクラスターのメトリクスを複合的に抽象化した値で、RuPerSecは1秒間にRUが補充される量を表します。 ↩︎

レバテック開発部

Discussion