転生じゃなかった・・・移行したら「TiDB Cloud Dedicated」だった件

本記事は [TiDB Advent Calendar 2024](https://qiita.com/advent-calendar/2024/tidb) 23日目の記事です。

# あらすじ
「データってさ、分散とかしなくても意外とイケるっしょ？」
データの管理？それくらい俺に任せとけと豪語していた平凡なデータベースサーバーだったが、突如現れた謎の鬼強ブラッククエリ軍団に押し潰されてあえなくクラッシュ。。。

「俺の人生、SQLに捧げただけだったな……」と思ったその瞬間、謎の声が響く。

「あなたの新しい人生、TiDBの世界で頑張ってみませんか？」

次に目覚めると、なんと「TiDB Cloud Dedicated」に移行していた！！

あれ。。。体はクラスタ構成、心は水平スケーリング、え？？
なぜか全身が分散システムでできていて、色々とチート級のスキルを持つ俺。

しかもNewSQLだし「俺って、強くね？」とこれは夢か・・・いやいや夢ではないこれは現実だ！！と気づくと同時に自信に満ち溢れていった。・・・続く

# はじめに

どうも！！urachoooooooです！！！

あらすじはさておきTiDB Cloud Dedicatedへ移行するための簡単な手順を載せていきますーー！！
（今回はサクッと書くのみです）

# 転生・・・ではなく移行の手順

:::note warn
今回使用するCloudプロバイダは「AWS」です！！
:::

## TiUP環境を作る
まずはお馴染みの「tiup」をインストールしてから、「dumpling」や「sync_diff_inspector」をインストールしていきます。
尚、今回「tidb-lightning」はマネージドを使用するのでインストールしないです。
（MySQLクライアントはインストールしておくと便利ですー）

### やること
>移行に必要なツールをインストール

### 手順で必要なもの

:::note info
- tiup
- dumpling
- sync_diff_inspector
:::

### 手順
- tiup
    ```bash:インストール
    curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh
    source /home/ec2-user/.bash_profile
    ```

- dumpling
    ```bash:インストール
    tiup install dumpling
    ```

- sync_diff_inspector
    ```bash:パラメータ（環境に合わせてください）
    TIDB_VERSION="v8.1.1"
    ARCH="amd64"
    ```

    ```bash:インストール
    wget https://download.pingcap.org/tidb-community-toolkit-${TIDB_VERSION}-linux-${ARCH}.tar.gz
    tar zxvf tidb-community-toolkit-${TIDB_VERSION}-linux-${ARCH}.tar.gz

    echo "export PATH=$PATH:/home/ec2-user/.tiup/tidb-community-toolkit-${TIDB_VERSION}-linux-${ARCH}" >> ~/.bash_profile
    source ~/.bash_profile

    sync_diff_inspector --version
    ```
    - TIDB_VERSION：使用しているTiDB Cloud Dedicatedのバージョンに合わせる
    - ARCH：使用環境に合わせる

## PrivateLinkでVPCと繋げる
TiDB Cloud Dedicatedへの接続はVPCピアリングとPrivateLinkの接続が可能になりますが、今回は「PrivateLink」で接続していきます

### やること

>AWSとTiDBのVPCの接続の確立

### 手順で必要なもの

:::note info
- TiDB
    - service-name
- AWS
    - VPC ID
    - サブネットID
    - セキュリティグループID
    - VPCエンドポイントID
:::

### 手順
#### 【①TiDBでの作業】

##### 1. TiDB Cloudコンソールを使用して対象のClusterの接続設定へ移動
- TiDB Cloudコンソールを開き、対象のClusterを選択する
- 「Connect」ボタンを押下する
- 表示された接続オプションから「Private Endpoint」を選択し、「Create Private Endpoint Connection」ボタンを押下する

##### 2. TiDBのサービス生成を待機
- 「Private Endpoint Connection」の作成処理が開始
    - 「TiDBのサービス」が生成されるまで待機する

##### 3. PrivateLinkの準備が完了したらAWS情報を入力
- PrivateLinkの準備が整ったら、AWSの「VPC ID」と「サブネットID」を入力する
- 入力した情報を基に、AWS CLIで使用するコマンドが生成されます

##### 4. サービス名を控える
- 生成されたサービス名が以下の形式で表示されます
    - 「com.amazonaws.vpce.xx-xxxx-x.vpce-svc-xxxxxxxxxxxxxxxxx」の様な形式です

#### 【②AWSでの作業】

##### 5. AWSマネジメントコンソールを使用してVPCエンドポイント作成へ移動
- AWSマネジメントコンソールを開き、「VPC」を選択する
- メニューから「エンドポイント」を選択し、「エンドポイントを作成」ボタンを押下する

##### 6. TiDBのサービス名を使用して検証
- TiDB Cloudで控えた「service-name」を使用して、サービスの検証を行う

##### 7. ネットワークの設定
- 使用する「VPC ID」、「サブネットID」と「セキュリティグループID」を設定する
- 必要な項目を入力後、「エンドポイントを作成」ボタンを押下する

    :::note warn
    TiDB Cloud Serverlessとは異なり、追加設定でDNSの有効化はここではしない。有効化すると作成時にエラーになります

##### 8. VPCエンドポイントIDを控える
- 作成されたVPCエンドポイントのIDを控える（このIDは次に行うTiDBの設定で必要になります）

#### 【③TiDBでの作業】

##### 9. AWSのプライベートエンドポイントへの接続

TiDBのサービス名を控えた所から引き続き実施していきます
- AWSで控えた「VPCエンドポイントID」を設定してAWSのプライベートエンドポイントへの接続を作成する
    - 接続するとAWSで作成したエンドポイントのステータスが保留中に変更
- しばらくするとAWSのプライベートエンドポイントへの接続が完了する
    - TiDBへの様々な接続方法が表示される様になります

#### 【④AWSでの作業】
##### 10. エンドポイントの有効化
:::note warn
AWSで作成したエンドポイントのステータスが「保留中」から「使用可能」になったことを確認してください
:::

- プライベートDNS名を変更するから「このエンドポイントで有効にする」にチェックを入れて変更を保存する
- ステータスが「保留中」から「使用可能」になったことを確認する

#### 【⑤TiDBでの作業】
##### 11. 接続確認
- 対象のClusterへ接続できることを確認する
    ```bash
    mysql --comments -u 'root' -h privatelink-xxxxxxxx.xxxxxxxxxxxx.clusters.tidb-cloud.com -P 4000 -D 'test' -p'<PASSWORD>'
    ```

## TiDBからS3への接続許可設定
dumplingとTiDB Lightning（マネージド）を使用したデータ移行を行うためにdumplingを使用したインポートの許可とLightningを使用したエクスポートの許可を行う必要があるためその設定を行います

### やること

>TiDBからAWS S3へのアクセス許可設定

### 手順で必要なもの

:::note info
- TiDB
    - TiDB Cloud Account ID
    - TiDB Cloud External ID
- AWS
    - S3のARN
    - S3ロールのARN
:::

### 手順
:::note warn
事前にS3バケットとフォルダの作成を行なっておいてください
:::

#### 【①TiDBでの作業】

##### 1. TiDB Cloud Dedicatedのクラスターを選択
- TiDB Cloudコンソールで対象のDedicatedクラスターを選択する
- 「Import」メニューを選択し、「S3からデータをインポート」を選択する

##### 2. **TiDB Cloudアカウント情報の取得**
- TiDB Cloudから「TiDB Cloud Account ID」と「TiDB Cloud External ID」を取得する
- 取得した「TiDB Cloud Account ID」と「TiDB Cloud External ID」を控える

#### 【②AWSでの作業】
##### 3. S3のARNをコピー
- 対象のS3バケットのARNを確認し、コピーする

##### 4. IAMポリシーを作成
- S3アクセス許可を設定するIAMポリシーを作成する
    - ActionにS3バケットへの必要なアクセス権限を設定
    - Resourceに「S3のARN」を設定

##### 5. IAMロールを作成
- IAMロールを作成する
    - 作成時に、控えた「TiDB Cloud Account ID」と「TiDB Cloud External ID」を使用して設定
   - 作成したIAMポリシーをこのロールにアタッチする

##### 6. IAMロールのARNをコピー
- 作成した「S3ロールのARN」を控える（このARNは次に行うTiDBの設定で必要になります）

## TiDB Cloud Dedicatedへデータ移行
上記手順でデータを移行する事前準備が整ったためデータを移行していきます。

DumplingからS3へSQL形式でアップロードして、TiDB Clooud Dedicatedのインポート（TiDB Lightning：physical）を使用してデータ移行を行います。

データ移行の対象はTiDBとの互換性のあるMySQLやAuroraのデータベースから移行する想定です

### やること

>TiDBとの互換性のあるデータベースからTiDB Cloud Dedicatedへのデータ移行

### 手順で必要なもの

:::note info
- TiDBへ移行するためのテーブル定義
- S3へアクセスするための捨てのアクセスキー
- S3のURL
- S3ロールのARN
- dumplingの移行対象マッピング
:::

### 手順
:::note warn
- TiDB Cloud Dedicatedを使用するには事前にクレジットカードの登録或いは、AWSマーケットプレイスへの設定を行なう必要があります
:::
##### 1. TiDB Cloud Dedicated作成
- TiDB Cloud Dedicatedの設定
    - Choose a Tier：Dedicated
    - Cloud Provider：AWS
    - Region：環境に合わせて設定
    - ストレージ：環境に合わせて設定
    - Cluster Size
        - TiDB Node：環境に合わせて設定
        - TiKV Node：環境に合わせて設定（3台1グループでスケール）
- 作成する
    - 上記設定を確認後、TiDB Cloud Dedicatedクラスターを作成する
##### 2. TiDB用テーブル定義作成
- mysqldumpやdumplingを使用してテーブル定義を取得する
- AUTO_INCREMENTをAUTO_RANDOMに変更するなどTiDBのホットスポットを回避する設定を事前に行う

##### 3. TiDBへテーブル定義をリストア
- データベースを作成する
- 作成したテーブル定義をリストアする

##### 4. 移行元のデータをエクスポート
:::note info
- 「xxx」を環境に合わせて設定してください
- FILE_TYPEは「sql」で設定していますが「csv」でもどちらでもOKです
- CONSISTENCYは「auto」にしていますが必要に応じて設定してください
- OUTPUTは「S3のURL」を設定してください
:::
- 捨てのアクセスキーを環境変数へ登録する
    ```bash
    export　AWS_ACCESS_KEY_ID=xxx
    export AWS_SECRET_ACCESS_KEY=xxx
    export AWS_REGION=xxx
    ```
- パラメータ
    ```bash
    HOST_NAME="xxx"          # 移行元のホスト名
    USER="xxx"               # 移行元のユーザー名
    PASS="xxx"               # 移行元のパスワード
    PORT=3306                # 移行元のポート番号
    DATABASE="xxx"           # エクスポートするデータベース名
    FILE_TYPE="sql"          # ファイル形式 (SQL または CSV)
    THREADS=xxx              # スレッド数
    ROWS=xxx                 # テーブル内同時実行数
    CONSISTENCY="auto"       # 一貫性設定
    OUTPUT="s3://xxx"        # エクスポート先S3のURL
    ```
    - --no-schemas：テーブル定義なし
    - -t（--threads）：コア数に合わせる
    - -r（--rows）：テーブル内同時実行数（0無効、1以上は有効）
    - -B（--database）：指定されたデータベースをエクスポートする
        - 複数選択する場合は「--filter」を使用する
- コマンド
    ```bash
    time tiup dumpling --no-schemas -u${USER} -p${PASS} -P${PORT} -h${HOST_NAME} --database ${DATABASE} --filetype ${FILE_TYPE} --threads ${THREADS} --rows ${ROWS} --consistency ${CONSISTENCY} --output ${OUTPUT} -F256MiB
    ```

##### 5. S3のデータをインポート
:::note warn
TiDBのデータが0件であることを確認する
（移行先のデータが存在する場合は削除する）
:::

- TiDB Cloud Dedicatedのインポート設定を行いConnectボタンを押下する
    - インポート設定
        - スキーマ：なし
        - フォーマット：SQL
        - フォルダーURI：S3のURL
        - Role ARN：S3ロールのARN
- 移行対象のテーブルにチェックを入れ、「Start Import」を押下してインポートを開始する
- ステータスが「Completed」になることを確認する





## 移行データチェック
TiDB Cloud Dedicatedのインポート（Lightning）で移行した場合でもテーブル定義のチェックやデータの整合性は行うのですが、おまけとして「sync_diff_inspector」でもチェックを行なっていきます（通常はインポート時にエラーになった場合にどこでエラーになったのか？を特定する場合に使うと良いかと思います！！）

### やること

>データ移行元と移行先のテーブル定義とデータを比較

### 手順で必要なもの

:::note info
- sync_diff_inspectorのTOML
:::

### 手順
- 実行
    ```bash
    time sync_diff_inspector --config=sync_diff_inspector.toml
    ```
- TOMLサンプル（xxxを環境に合わせて設定する）
    ```toml:sync_diff_inspector.toml
    # Diff Configuration.
    
    ######################### Global config #########################
    # The number of goroutines created to check data. 
    # The number of connections between sync-diff-inspector 
    # and upstream/downstream databases is slightly greater than this value.
    check-thread-count = xxx

    # If enabled, SQL statements is exported to fix inconsistent tables.
    # 有効にすると、矛盾したテーブルを修正するためにSQLステートメントがエクスポートされます。
    export-fix-sql = true

    # Only compares the table structure instead of the data.
    # データではなくテーブル構造のみを比較します。
    check-struct-only = false

    # If enabled, sync-diff-inspector skips checking tables that do not exist in the upstream or downstream.
    # 有効にすると、sync-diff-inspectorはアップストリームまたはダウンストリームに存在しないテーブルのチェックをスキップします。
    skip-non-existing-table = false

    ######################### Datasource config #########################
    [data-sources]
    [data-sources.mysql]
    host = "xxx"
    port = 3306
    user = "xxx"
    password = "xxx"

    [data-sources.tidb]
    host = "xxx"
    port = 4000
    user = "xxx"
    password = "xxx"

    ######################### task config #########################
    # Configures the tables of the target database that need to be compared.
    [task]
    # output-dir saves the following information:
    output-dir = "/var/tmp/sync_diff_inspector_output"

    # The upstream database. The value is the unique ID declared by data-sources.
    source-instances = ["mysql"]

    # The downstream database. The value is the unique ID declared by data-sources.
    target-instance = "tidb"

    # The tables of downstream databases to be compared. Each table needs to contain the schema name and the table name, separated by '.'
    # Use "?" to match any character and "*" to match characters of any length.
    # For detailed match rules, refer to golang regexp pkg: https://github.com/google/re2/wiki/Syntax.
    target-check-tables = ["database.*"]

    ######################### Table config #########################
    # Special configurations for specific tables. The tables to be configured must be in `task.target-check-tables`.
    # config1  is the only custom ID for this configuration. It is used for the above `task.target-configs` configuration.
    [table-configs.config1] 
    # The name of the target table, you can use regular expressions to match multiple tables, but one table is not allowed to be matched by multiple special configurations at the same time.
    target-tables = ["database.table"]

    # (optional) Specifies the column used to divide data into chunks. If you do not configure it,
    index-fields = ["id"]
    ```

# まとめ
TiDB Cloud Dedicatedへ移行する簡単な手順を書きました！！

書いたのですが、やはりイメージがないと伝わりにくいかと思いますので、イメージを使用したもっと詳細な情報はいつかどこかで公開できれば💦

また、DumplingやTiDB Lightningは移行速度が本当に早いのでまたの機会で速度の計測なども載せられたら！！