Alibaba Cloud DTSによってサービスダウンタイムなしのデータ移行

# 1. DTSとは
DTS（Data Transmission Service）は、リレーショナルデータベース、NoSQL、OLAPなど様々なデータベース間でのデータ移行やリアルタイムデータ同期を実現するAlibaba Cloudのマネージドサービスです。
AWSのDMS（Database Migration Service）に相当しますが、DTSはより多様な機能を提供します。

https://www.alibabacloud.com/ja/product/data-transmission-service

DTSは、次のような主要機能を提供します。

https://www.alibabacloud.com/help/en/data-transmission-service/latest/what-is-data-transmission-service

- `Data migration`: データソースにあるスキーマや既存のデータやキャプチャされた変更などをターゲットデータベースへ移行する機能
    - オンプレや他社のクラウド上のデータベースをAlibaba Cloudへ移行する時に利用
    - データ移行タスクは、作業完了後に削除することが一般的です。これは、一度きりのデータ移行に最適なアプローチです。
- `Data synchronization`：リアルタイムにdata sourcesを`双方向`同期する機能、以下のシナリオで利用されことが多いです。(一回きりのタスクではなく、常時稼働なタスク)
    - DRサイトの構築(マインのサイトから継続的にデータをDRサイトへリアルタイム同期)
    - multi-site(DR用ではなく、２つのサイトは同時にサービスを提供し、end userからのリクエストは自動的に地理的に近いサイトに振り分けられる)間のデータのリアルタイム同期
    - 業務用データベース(OLTPのデータベース)からデータをdata warehouseへ同期
- `Change tracking`: データソースのdataの変更をキャプチャし、publisher/subscriber streamとしてメッセージを第三者に送る
    - 複数のsubscriberに同時にメッセージを送れるのは特徴です。(AWSのSNSに似ているところがあります。)

今回は`Data migration`を中心として、説明します。

# 2.  `Data migration`の仕組み
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/8ad2f13f-67da-7ba7-11e9-8b51c819387d.png)

- ポイントとしては、初期データ（既存データ）の同期と変更されたデータの同期を別々のサブタスクとして実行します。厳密に言うと、「スキーマのマイグレーション」という選択肢もあります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/7b139d91-0dfb-26db-c5f2-0080b288cf0e.png)

- 実行順序としては、スキーマのマイグレーション　→　初期データのマイグレーション　→　増分データのマイグレーションという順に行います
- 3つのサブタスクは単独で（つまり、スキーマのみ同期、または初期データのみ同期、または増分データのみの同期）実行も可能ですが、一般的には3つすべてを選択することをおすすめします

:::note info
初期データのみを同期し、変更されたデータを同期しない方法を選ぶと、データ移行時にソースデータベース側のアプリケーションを完全に停止し、データの変更を行わないようにする必要があります。データ量が多い場合、移行には数十時間かかることがあり、その間はサービスが完全に停止してしまいます。そのため、可能な限り「増分データの同期」も選択することをおすすめします。
:::

- データをそのまま同期するのではなく、DTSの`ETL機能`を通じてデータをフィルタリングや加工を行い、その後ターゲットデータベースに書き込むことも可能です。
:::note info
- フィルタリング：例えば、一定の条件を満たすデータしか同期しない
:::

- データ検証：データ同期プロセスにおけるレコードの欠損、特定カラムの値の漏れ、またはスキーマ（カラム）に関する問題を人手で確認することは非常に困難です。このため、データ検証（data verification）機能が役立ちます。この機能を活用すると、ソースデータベースとターゲットデータベースの各行のhash値を計算し、両者が一致しているかどうかをチェックします。全てが一致していれば、ソースデータベースとターゲットデータベースのデータが完全に同じであることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/8e0f0b48-5845-ed7e-5030-3d5d88f7bee8.png)

:::note info
- 単純に両データベースの行数が一致しているかどうかの検証も可能です。ソースデータベースとターゲットデータベースが異なるエンジンを使用している場合（例：OracleからPolarDB for Oracleに移行）、データの一部が変換される可能性があるため、行数の一致のみを確認することが推奨されます。
- 料金：
    - 行ごとのhash値の比較：有料
    - 行数のみ検証：無料
    - 既存の全文データおよび差分データの検証：有料
    - schema verification: 無料
:::

https://www.alibabacloud.com/help/en/dts/user-guide/billing-method-of-data-verification

# 3. データ移行の流れ
https://www.alibabacloud.com/help/en/dts/use-cases/switch-workloads-to-the-destination-database

1. DTSタスクを作成し、データをリアルタイムにsource databaseからtarget databaseに同期
    1. 移行タイプ：以下の全部を選択することがおすすめです
        - スキーマの移行
        - 既存データの移行
        - 増分データの移行
    1. データ検証モード：以下の全部を選択することがおすすめです
        - 既存データの検証
        - 増分データの検証
        - スキーマの検証
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/8580440e-ba37-12f5-645f-53d444b538d7.png)
1. データのマイグレーションが完了したら、データ検証を行い、欠損や漏れがないかを自動チェック
    1. スキーマの移行が完了したら、スキーマ検証のsub taskが自動的に起動し、検証を行う
    1. 既存データの移行が完了したら、既存データに対するデータ検証のsub taskが自動的に起動し、検証を行う
    1. 増分データ同期しながら、内容のhash値を比較し、sourceとtargetのデータが一致しているかどうかを自動的にチェック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/fde3a6eb-2939-ac54-cb7b-37c9a99ec814.png)
1. サービス(Application)を一時に停止し、source databaseにデータを書き込まないようにする
1. 増分データの移行と増分データの検証を除くすべてが完了し、さらに増分データの遅延が約1秒程度になった場合、すべての同期対象が同期されたとみなします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/354f423b-d329-7aff-c105-66111cbca33b.png)
1. ソースデータベースにログインし、セッション情報を確認し、新しいセッションが書き込み操作を実行していないことを確認します
    ```
    MySQLの場合：
    show processlist;
    ```
1. アプリケーション側のdatabase接続endpointをsource databaseからtarget databaseに変更
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/b0fc0950-756f-c3ba-37d3-0cf77c3b622c.png)
1. 新データベースでアプリケーションの動作確認
1. 全て問題ないことが確認できた後、DTS taskの削除とsource databaseの停止などを行う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/c108ddae-14b9-662c-1eb7-cf85f64bc7b6.png)

# 4. `Data migration`の利用パターン
1. Alibaba Cloud内部のマネージドデータベース間のデータ同期
例えば RDS → PolarDB あるいは PolarDB → RDS
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/b16b2838-3827-5e92-9e66-7fdd65e355b1.png)

2. Alibaba CloudのECS上のデータベースからマネージドデータベースへのデータ同期
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/f7d1fdbd-b2d4-52e2-07f8-54f0093e3484.png)

3. オンプレからAlibaba Cloud上のマネージドデータベースへのデータ同期
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/b0b43c9a-bba9-eaa8-ed53-fab056c77776.png)

4. 他のクラウド上のデータベースからAlibaba Cloud上のデータベースへのデータ同期
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/fa878870-a645-867f-f358-450df26c9f9b.png)

5. Alibaba Cloud以外のクラウド同士のデータベースのデータ同期
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/11b9d5a0-22e7-d0a3-3f4c-290938e52e34.png)
今回は利用パターンの中の一つ目の Alibaba Cloud内部のマネージドデータベース間のデータ同期について解説します。

# 5. RDSからPolarDBへのデータマイグレーション
## 5.1 前提
- RDS MySQLとPolarDB MySQLのバージョンを統一することが推奨されます。これにより、互換性の問題を防ぎます
- region, VPC, Alibaba Accountなどは同じにする必要がありません

## 5.2 事前準備
1. RDS
- ローカルPCのmysql clientからRDSとPolarDBにアクセスさせるため、 public endpointを作成しておく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/d2a1c033-71ac-4195-39c3-8d73ff774fd1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/d2a3d243-910a-6b24-58f4-d6515789838b.png)
- whitelistにPCのpublic IPを追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/1ebe9af0-d381-d486-9be9-b524f14e0f95.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/773daaad-cba3-60c8-9397-05dc8a8f426f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/77f592c0-29af-232d-4bc7-b8c45b5572b4.png)
- 追加されたIP whitelist
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/dc8ffa3c-a80e-6cce-8efa-2e018bc0577e.png)


- データの準備
    ```
    mysql> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | __recycle_bin__    |
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    +--------------------+
    
    mysql> create database dts_test;
    
    mysql> use dts_test;
    Database changed
    mysql> show tables;
    
    mysql> CREATE TABLE myguests (
        ->     id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        ->     firstname VARCHAR(30) NOT NULL,
        ->     lastname VARCHAR(30) NOT NULL,
        ->     email VARCHAR(50),
        ->     reg_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
        ->     );
    
    
    mysql> show tables;
    +--------------------+
    | Tables_in_dts_test |
    +--------------------+
    | myguests           |
    +--------------------+
    
    mysql> insert into myguests(firstname,lastname,email) values("user1","","user1@example.com");
    
    mysql> insert into myguests(firstname,lastname,email) values("user2","","user2@example.com");
    
    mysql> select * from myguests;
    +----+-----------+----------+-------------------+---------------------+
    | id | firstname | lastname | email             | reg_date            |
    +----+-----------+----------+-------------------+---------------------+
    |  1 | user1     |          | user1@example.com | 2024-02-19 13:49:25 |
    |  2 | user2     |          | user2@example.com | 2024-02-19 13:49:31 |
    +----+-----------+----------+-------------------+---------------------+
    ```

2. PolarDB：
- public endpoint作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/3747e23f-7ab3-ff9d-3da9-548fa0b7f87b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/33a66c5a-aa7d-7b80-f6b6-51f3e436a2d2.png)
- PCのpublic IPをwhitelistに追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/027a3695-7de7-d40e-353c-91e4a2d0e281.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/1f8fb6b0-6d6a-8dca-945f-3692af05dcca.png)

- データベースの中身を確認(`dts_test`というデータベースが存在していない)
    ```
    mysql> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | __recycle_bin__    |
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    +--------------------+
    ```

## 5.3 DTSタスク作成
- `Data Migration`タスクを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/ddb77f95-1c58-c42c-4ac7-dcaa58c4d9ea.png)
- source databaseのRDSとtarget databaseのPolarDBの情報を入力し、DTSからそれぞれ接続できるかどうかを確認(`Test Connectivity and Proceed`ボタンを押下)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/54de1f31-86d5-2bf9-0e8c-dd06b7ad2548.png)

:::note info
- DTSからアクセスできるために、DTS実行しているサーバー群のIPが自動的にRDSとPolarDBインスタンスのwhitelistに追加されました
    - RDS:
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/ea0e291c-cc14-2871-a7aa-e59c0e60c4c5.png)
    - PolarDB:
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/baaf7295-6ab0-9a02-d683-fcf008229b5b.png)
:::

- デフォルト設定では、既存データの全量移行（`Full Data Migration`）のみが選択されています。しかし、スキーマ情報（テーブル構成）と差分データの同期も行いたい場合は、スキーマ移行（`Schema Migration`）と増分データ移行（`Incremental Data Migration`）も追加で選択することをお勧めします。
:::note info
全量データ移行のみを選択した場合、サービスの切り替え前には、すべてのデータが同期されるまでサービスを完全に停止する必要があります。サービスのダウンタイムを最小限に抑えたい場合は、スキーマ移行、全量データ移行、増分データ移行の3つを選択することが推奨されます。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/d1feffda-c64f-a5fc-33a3-da5c914d22a4.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/15ffda42-45ac-776d-e2b7-07cb1e8f0ac5.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/19f866f7-c801-668e-d477-2231ea5df099.png)
- `Advanced Configurations`で全部デフォルトのままにする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/0fdeec7a-ee33-69fc-cf30-2fb481e13623.png)

- data verificationの種類を選択：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/c86e4ca0-e633-6318-4b38-4389bfdfdba2.png)


- DTSインスタンスのクラスを選択

https://www.alibabacloud.com/help/en/dts/product-overview/specifications-of-data-migration-instances#concept-26606-zh

:::note info
DTSインスタンスのクラスは増分データ同期する時の最大`RPS(record per second)`に影響します。
例えばソースデータベースからターゲットテーブルに毎秒5000行のデータが増分同期される場合、RPSは5000です。
今回はテストのため、一番小さい`small`インスタンスタイプを選択します。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/b2df4dba-c3f1-005e-0ee4-b7d559e04501.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/be86b4ec-2ccc-5ecf-9906-da63ebb23fac.png)
- 生成されたタスク
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/f34e4e0e-b59f-4abc-7ce2-9ae99778dc5d.png)

- デフォルトでは終了した子タスクが表示されない。全体のプロセスを確認するため、`Hide Completed Modules`のチェックを外します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/53299fd6-d2bd-96fd-496a-ac8836318600.png)
- 赤い部分はすでに完了している子タスクであり、青い子タスクは実行中になります。
:::note info
増分データはいつでも来る可能性があるため、それに対応する子タスクは基本的に常時実行状態を維持します。
:::
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/b43a5a99-5b2d-dff1-f7e8-29594bfd2151.png)

## 5.4 DTSの動作確認
### 5.4.1 既存データの同期
```
PolarDB：

mysql> show tables;
+--------------------+
| Tables_in_dts_test |
+--------------------+
| myguests           |
+--------------------+

mysql> select * from myguests;
+----+-----------+----------+-------------------+---------------------+
| id | firstname | lastname | email             | reg_date            |
+----+-----------+----------+-------------------+---------------------+
|  1 | user1     |          | user1@example.com | 2024-02-19 18:30:22 |
|  2 | user2     |          | user2@example.com | 2024-02-19 18:30:26 |
+----+-----------+----------+-------------------+---------------------+
```
### 5.4.2 差分データの同期
https://www.alibabacloud.com/help/en/dts/user-guide/migrate-data-from-a-self-managed-mysql-database-to-an-apsaradb-rds-for-mysql-instance-1

:::note info
差分データの範囲：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/15e23393-067a-2715-4465-a154949f7e64.png)
:::

1. DML: INSERT文
    ```
    RDS:
    mysql> insert into myguests(firstname,lastname,email) values("user3","","user3@example.com");
    
    mysql> select * from myguests;
    +----+-----------+----------+-------------------+---------------------+
    | id | firstname | lastname | email             | reg_date            |
    +----+-----------+----------+-------------------+---------------------+
    |  1 | user1     |          | user1@example.com | 2024-02-19 18:30:22 |
    |  2 | user2     |          | user2@example.com | 2024-02-19 18:30:26 |
    |  3 | user3     |          | user3@example.com | 2024-02-19 19:17:54 |
    +----+-----------+----------+-------------------+---------------------+
    
    PolarDB: `user3`のデータ自動的に同期された
    mysql> select * from myguests;
    +----+-----------+----------+-------------------+---------------------+
    | id | firstname | lastname | email             | reg_date            |
    +----+-----------+----------+-------------------+---------------------+
    |  1 | user1     |          | user1@example.com | 2024-02-19 18:30:22 |
    |  2 | user2     |          | user2@example.com | 2024-02-19 18:30:26 |
    |  3 | user3     |          | user3@example.com | 2024-02-19 19:17:54 |
    +----+-----------+----------+-------------------+---------------------+
    ```


2. DML: UPDATE文
    ```
    RDS:
    mysql> update myguests set firstname="user1_new" where id = 1;
    
    mysql> select * from myguests;
    +----+-----------+----------+-------------------+---------------------+
    | id | firstname | lastname | email             | reg_date            |
    +----+-----------+----------+-------------------+---------------------+
    |  1 | user1_new |          | user1@example.com | 2024-02-19 19:21:01 |
    |  2 | user2     |          | user2@example.com | 2024-02-19 18:30:26 |
    |  3 | user3     |          | user3@example.com | 2024-02-19 19:17:54 |
    +----+-----------+----------+-------------------+---------------------+
    
    PolarDB: id=1のデータが更新されたました
    mysql> select * from myguests;
    +----+-----------+----------+-------------------+---------------------+
    | id | firstname | lastname | email             | reg_date            |
    +----+-----------+----------+-------------------+---------------------+
    |  1 | user1_new |          | user1@example.com | 2024-02-19 19:21:01 |
    |  2 | user2     |          | user2@example.com | 2024-02-19 18:30:26 |
    |  3 | user3     |          | user3@example.com | 2024-02-19 19:17:54 |
    +----+-----------+----------+-------------------+---------------------+
    ```


3. DML: DELETE文
    ```
    mysql> delete from myguests where id = 3;
    
    mysql> select * from myguests;
    +----+-----------+----------+-------------------+---------------------+
    | id | firstname | lastname | email             | reg_date            |
    +----+-----------+----------+-------------------+---------------------+
    |  1 | user1_new |          | user1@example.com | 2024-02-19 19:21:01 |
    |  2 | user2     |          | user2@example.com | 2024-02-19 18:30:26 |
    +----+-----------+----------+-------------------+---------------------+
    
    mysql> select * from myguests;
    +----+-----------+----------+-------------------+---------------------+
    | id | firstname | lastname | email             | reg_date            |
    +----+-----------+----------+-------------------+---------------------+
    |  1 | user1_new |          | user1@example.com | 2024-02-19 19:21:01 |
    |  2 | user2     |          | user2@example.com | 2024-02-19 18:30:26 |
    +----+-----------+----------+-------------------+---------------------+
    ```


4. DDL: CREATE TABLE
    ```
    RDS:
    mysql>  CREATE TABLE items(id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(30) NOT NULL, price VARCHAR(30) NOT NULL);
    
    mysql> show tables;
    +--------------------+
    | Tables_in_dts_test |
    +--------------------+
    | items              |
    | myguests           |
    +--------------------+
    
    PolarDB:
    mysql> show create table items;
    +-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Table | Create Table                                                                                                                                                                                    |
    +-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | items | CREATE TABLE `items` (
      `id` int(6) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(30) NOT NULL,
      `price` varchar(30) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
    +-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    
    RDS:
    mysql>  insert items(id, name, price) values(1, "macbook", 200000);
    
    mysql> select * from items;
    +----+---------+--------+
    | id | name    | price  |
    +----+---------+--------+
    |  1 | macbook | 200000 |
    +----+---------+--------+
    
    PolarDB:
    mysql> select * from items;
    +----+---------+--------+
    | id | name    | price  |
    +----+---------+--------+
    |  1 | macbook | 200000 |
    +----+---------+--------+
    ```


5. DDL: ALTER TABLE
    ```
    RDS:
    mysql> alter table items add (category VARCHAR(30));
    
    PolarDB:
    mysql> show create table items;
    +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Table | Create Table                                                                                                                                                                                                                                            |
    +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | items | CREATE TABLE `items` (
      `id` int(6) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(30) NOT NULL,
      `price` varchar(30) NOT NULL,
      `category` varchar(30) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 |
    +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    
    RDS:
    mysql> insert items(id, name, price, category) values(2, "HP ProBook", 150000, "PC");
    
    PolarDB: 新しく追加された`category`カラムにデータも入りました
    mysql> select * from items;
    +----+------------+--------+----------+
    | id | name       | price  | category |
    +----+------------+--------+----------+
    |  1 | macbook    | 200000 | NULL     |
    |  2 | HP ProBook | 150000 | PC       |
    +----+------------+--------+----------+
    ```

### 5.4.3 data verification
#### 5.4.3.1 一致しているケース
- 既存のデータ(`Full Data`)のverification
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/f25c6268-ca28-f0dc-2c99-ac46769f1662.png)
:::note info
task起動した最初は`myguests`テーブルに2件のデータしかなかったため、既存データの部分は2件がチェックされ、一致していないレコードがありません。(一致していないレコードがある場合は`Actions`欄に内容が表示されます)
:::

- schemaのverification:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/7761d533-3b95-85c2-b6cb-2dfff037da9e.png)
:::note warn
tableやviewやFunctionなどがチェックの対象になります。今回はテーブルしかありません。しかもチェックした結果は一致していないテーブルがありません。
:::

- 増分データのverrification:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/cd2174be-8996-3812-e010-02e2c23beea8.png)
:::note info
- `mygusts`テーブル： insert一件、update一件、delete一件 合計3件のデータがチャックされて、一致していないレコードが存在していません。
- `items`テーブル： insert 二件のデータがチェックされて、一致していないレコードが存在していません。
:::

#### 5.4.3.2 一致していないケース
:::note info
上記では、全てが一致する状況を説明しました。次に、データが一致しない場合、data verification機能がそれを正確に検出できるかどうかを検証します。
通常、ターゲットデータベース（PolarDB）のデータを直接変更することは推奨されませんが、不一致の状況を模倣するためには、このような直接的な変更を行うことにします。
:::

- PolarDBのデータを直接変更
    ```
    PolarDB:
    mysql> update items set category = "PC" where id = 1;
    
    mysql> select * from items;
    +----+------------+--------+----------+
    | id | name       | price  | category |
    +----+------------+--------+----------+
    |  1 | macbook    | 200000 | PC       |
    |  2 | HP ProBook | 150000 | PC       |
    +----+------------+--------+----------+
    
    RDS:
    mysql> select * from items;
    +----+------------+--------+----------+
    | id | name       | price  | category |
    +----+------------+--------+----------+
    |  1 | macbook    | 200000 | NULL     |
    |  2 | HP ProBook | 150000 | PC       |
    +----+------------+--------+----------+
    ```
- `Full Data Verification`の子タスクを再実行(`Restart`)させる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/43da1043-b1d0-387c-b6f5-59448e621927.png)
- `items`テーブルに一件一致していないデータが存在している。`view report`リンクをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/2959cbf9-e99a-6cb1-c0e7-f95f0431f369.png)
- `id`=1のデータの`category`というカラムにもとデータが`NULL`であり、targetデータは`PC`になっていることが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/ae60c9b6-9b79-f985-f0b6-811c4a354673.png)
- 再度PolarDBのデータを直接変更し、もとの状態に戻させる
    ```
    PolarDB:
    mysql> update items set category = null where id = 1;
    
    mysql> select * from items;
    +----+------------+--------+----------+
    | id | name       | price  | category |
    +----+------------+--------+----------+
    |  1 | macbook    | 200000 | NULL     |
    |  2 | HP ProBook | 150000 | PC       |
    +----+------------+--------+----------+
    
    RDS:
    mysql> select * from items;
    +----+------------+--------+----------+
    | id | name       | price  | category |
    +----+------------+--------+----------+
    |  1 | macbook    | 200000 | NULL     |
    |  2 | HP ProBook | 150000 | PC       |
    +----+------------+--------+----------+
    ```
- 再度`Full Data Verification`の子タスクを再実行(`Restart`)させる。すべてのデータが一致になりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/77a9b03a-4e15-53e5-cd1e-03e18bfd1781.png)

### 5.4.4 ETLでデータを加工してからターゲットデータベースに書き込む
#### 5.4.4.1 ETLの動き
今度はソースデータベースの`item`テーブルの`category`カラムにNULLを`other`という文字列に変更したうえで、ターゲットデータベースに書き込みます。
- DTSタスクを修正し、ETL設定を変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/a5e0c662-b47b-6f8e-e425-b2ee111b640d.png)
- ETLに以下の変更内容を入力
```
e_if(op_is_null(`category`), e_set(`category`,"other"))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/9d85644f-dc14-5e03-a555-57c5390ebedc.png)

- ソースデータベースに新しいレコードを作成します
```
mysql> select * from items;
+----+------------+--------+----------+
| id | name       | price  | category |
+----+------------+--------+----------+
|  1 | macbook    | 200000 | NULL     |
|  2 | HP ProBook | 150000 | PC       |

mysql> insert items(id, name, price) values(3, "chromebook", 32800);
Query OK, 1 row affected (0.01 sec)

mysql> select * from items;
+----+------------+--------+----------+
| id | name       | price  | category |
+----+------------+--------+----------+
|  1 | macbook    | 200000 | NULL     |
|  2 | HP ProBook | 150000 | PC       |
|  3 | chromebook | 32800  | NULL     |
+----+------------+--------+----------+
```
- ターゲットデータベースにNULLが`other`という文字列に変更されました(既存のデータは変更の対象外なので、id=1のデータは変わっていません)
```
mysql> select * from items;
+----+------------+--------+----------+
| id | name       | price  | category |
+----+------------+--------+----------+
|  1 | macbook    | 200000 | NULL     |
|  2 | HP ProBook | 150000 | PC       |
|  3 | chromebook | 32800  | other    |
+----+------------+--------+----------+
```
#### 5.4.4.2 データ検証
- 増分データのチェックで新規挿入されたid=3のレコードでデータが一致していないことが分かります
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/2663da5e-f3fd-1635-ce03-6598e6122f39.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/67d3a39e-d033-e8a0-dc3c-40b39054f339.png)
:::note warn
  やはりETLでデータ変換された場合は、hash値での比較方法はかならず不一致になってしまうため、hash値の比較の代わりに、行数の一致のみを確認することがおすすめです。
:::

- `Full Data Verification`と`Incremental Data Verification`の子タスクを削除して、新しい子タスクを作り直します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/c323b2cb-890b-ce7c-6816-65d13f77b679.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/a85c55b9-0336-e671-cd9e-6d1f215a8b1a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/bd0d60a2-e878-a050-807d-ac847d13a159.png)

- データ検証の種類を選択し、「行数を検証」を選択
:::note info
増分データは常に増加しているため、増分データの行数が一致するかどうかを検証することは不可能(そもそも意味がない)です。ETLでデータを加工する場合、行数の検証に頼るしかありません。しかし、増分データの行数が一致するかどうかを確認すること自体が元々不可能であるため、増分データの検証は省略すべきです。省略しない場合、増分データの検証はハッシュ値の比較になり、結果は必ず不一致となってしまいます。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/2db78dec-3765-fece-4395-e1aa45bb9ded.png)

- 結果としては不一致のレコードが存在していない(つまりレコード数が同じです)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/1aced1b2-1ff2-dc69-c42a-0e62abd385bf.png)

:::note info
行数一致していないパターンの確認：

- target database (PolarDB):
    ```
    mysql> delete from items where id = 1;
    Query OK, 1 row affected (0.01 sec)
    
    mysql> select * from items;
    +----+------------+--------+----------+
    | id | name       | price  | category |
    +----+------------+--------+----------+
    |  2 | HP ProBook | 150000 | PC       |
    |  3 | chromebook | 32800  | other    |
    +----+------------+--------+----------+
    ```

- source database (RDS):
    ```
    mysql> select * from items;
    +----+------------+--------+----------+
    | id | name       | price  | category |
    +----+------------+--------+----------+
    |  1 | macbook    | 200000 | NULL     |
    |  2 | HP ProBook | 150000 | PC       |
    |  3 | chromebook | 32800  | NULL     |
    +----+------------+--------+----------+
    ```
- 既存データの検証の子taskを再起動させる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/feb7b0a2-23c1-a37c-e1a1-c01378490abe.png)
- source databaseに3件のデータがあり、target databaseに2件のデータしかなくて、1件の差があることになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/248709b0-cc67-3304-316c-03f9bc77518e.png)

:::

# 6. まとめ
- DTSを使用する際、既存データと増分データの両方を選択することで、サービスのダウンタイムを最小化しつつ効率的にデータ移行が可能です。
- 増分データを選択した場合、ソースデータベースで新規に作成されたデータ(<font color="red">`INSERT`</font>)、更新されたデータ(<font color="red">`UPDATE`</font>)、削除されたデータ(<font color="red">`DELETE`</font>)、さらには<font color="red">`CREATE TABLE`</font>や<font color="red">`ALTER TABLE`</font>や<font color="red">`CREATE INDEX`</font>などのDDL文も自動的に同期されます。
- DTSのdata verification機能を利用すると、データの漏れや差異が自動的に検出されるため、非常に便利です。
- DTSの`ETL機能`を利用すると、一部のデータをフィルタリングや加工を行い、その後ターゲットデータベースに書き込むことも可能です。