本番稼働していて負荷をかけられないTiDBへの追加移行におけるAWS DMSのチューニングTips集
この記事は レバテック開発部 Advent Calendar 2025 16日目の記事です。
はじめに
レバテック開発部、SREチームに所属している金澤です。
現在、レバテックではTiDBをマルチテナントで利用しています。
元々はAurora MySQLをメインで使用しており、移行にはAWS DMSを採用しました。
今回は、TiDB移行におけるAWS DMSを利用する事例が少なかったこともあり、工夫した点をまとめたいと思います!
↓TiDBに関連する過去の記事
前提
移行対象
- ソースデータベースはAurora MySQL
- ターゲットデータベースはTiDB Dedicated
- TiDBは本番で利用しているシステムが存在するため、過度な負荷をかけることができない
AWS上で必要な主なリソース
- ソースエンドポイント
- ターゲットエンドポイント
- レプリケーションインスタンス
- 移行タスク
TiDB特有の観点
移行用のユーザーとリソースグループを作成する
TiDBにはリソースグループと言われる、TiDBクラスター上のリソース配分を分割すること機能があります。
今回は、DMSで利用する移行用のユーザーとリソースグループを作成し、RuPerSec[1]をリソースグループに設定することで、本番稼働中のシステムにデータ移行による負荷の影響を与えないように調整しました。
移行用のNodeGroupを分離する
TiDBクラスター内で、クライアントとの接続を担当するTiDBノードをグルーピングする事が可能な機能が提供されています。
こちらも、複数の移行タスクを実行するため、本番稼働中のシステムに影響を与えないよう分離しました。
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へと切り替わります
- フルロードを実行
- フルロード開始以降の変更分はレプリケーションインスタンス内でキャッシュする
- フルロード完了
- フルロード開始以降の変更分を反映
- 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ファイルのサイズ上限値で、ターゲットエンドポイントに設定します。
一度の書き込み量が多いと、TiDB側でタイムアウトするためデフォルトの32MB→8MB(8192KB)に変更しました。
おわりに
DMSは設定項目が多い分、適切に利用するハードルが非常に高く感じました。
一方で柔軟性が非常に高く、しっかりとDeepDiveすれば、汎用的に利用できる素晴らしいツールであると改めて実感しました。
他の観点もあれば、是非コメント等でアドバイス頂けたらと思います!
参考リンク
-
RUはTiDBクラスターのメトリクスを複合的に抽象化した値で、RuPerSecは1秒間にRUが補充される量を表します。 ↩︎
Discussion
DD