TiDB DMを使ってMySQLと同期してみる

## はじめに

前の記事で紹介した、MySQLとTiDBのデータ移行を行うツールTiDB DMですが、
TiUP Playground 1.16.1 からPlaygroundに含まれるようになり、簡単に試せるようになりました。

本記事ではDMを使ってMySQLとTiDBを同期してみます。データ移行だけではなくて、MySQLに更新したデータもTiDBに同期されるのがわかると思います。

AWSのDMS(Data Migration Service)のようなツールですが、以外と簡単なのでぜひ試してみてください。

https://docs.pingcap.com/ja/tidb/stable/dm-overview

## 準備

本記事では MySQL をDockerを使って起動します。また、MySQL側にいれるサンプルデータとして、[Employeeデータベース](https://github.com/datacharmer/test_db) を利用します。あらかじめ用意しておきましょう。

TiUPとPlaygroundの設定については、下記の記事をご覧ください。

https://qiita.com/bohnen/items/a4e42cadeb8223b4a588

以前TiUPをインストールしている方は、Playgroundを最新にしておく必要があります。

```bash
> tiup update playground
```

## MySQLの起動とサンプルデータの読み込み

MySQLを起動します。MySQLのバイナリはDockerのv8.0.40を利用して、my.confだけ渡す形にします。
my.confは下記の通りです。ここで必要な設定は**binlogの形式をROW**にしておくことです。

これを、`conf.d` ディレクトリ下に置きます。

```my.conf
[mysqld]
bind-address     = 0.0.0.0
character-set-server=utf8
collation-server=utf8_bin
default-storage-engine=INNODB
transaction-isolation=READ-COMMITTED
server-id        = 100
binlog_format    = row
log_bin          = /var/lib/mysql/mysql-bin.log
```

次のようにしてMySQLを起動します。<password> は各自設定してください。

```bash
docker run --name mysql -v ./conf.d:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=<password> -d -p 3306:3306 mysql:8.0.40
```

:::note warn
TiDB DMは MySQL v8.4で廃止された `SHOW MASTER STATUS` をチェックに使っているため、ここではv8.0.40を利用しています。（このチェックは設定で実施しないようにすることが可能）
:::

起動したら、サンプルデータを投入します。サンプルデータのあるディレクトリに移動して

```bash
mysql -u root -h 127.0.0.1 -p<password> < employees.sql
```

これでMySQL側の準備は完了です。

## TiDB Playgroundの起動

TiDB Playgroundを起動します。TiDB PlaygroundをDMつきで起動するために、オプションをセットします。

```bash
> tiup playground v8.1.1 --dm-master 1 --dm-worker 1
Start pd instance: v8.1.1
Start tikv instance: v8.1.1
Start tidb instance: v8.1.1
Start dm-master instance: v8.1.1
Waiting for dm-master instances ready
127.0.0.1:8261 ... Done
Start dm-worker instance: v8.1.1
Waiting for tidb instances ready
127.0.0.1:4000 ... Done
Start tiflash instance: v8.1.1
Waiting for tiflash instances ready
127.0.0.1:3930 ... Done

🎉 TiDB Playground Cluster is started, enjoy!

Connect TiDB:    mysql --host 127.0.0.1 --port 4000 -u root
Connect DM:      tiup dmctl --master-addr 127.0.0.1:8261
TiDB Dashboard:  http://127.0.0.1:2379/dashboard
Grafana:         http://127.0.0.1:3000
```

これで、TiDBプロセス一式とDMが起動しました。

## DM設定ファイルの準備

DMを利用するためには、ソースDBの定義とタスク定義を作成する必要があります。

### ソースDBの定義

ソースDBでは、MySQL側の接続設定を記載します。パスワードは先程Dockerで起動したMySQLに設定したものです。

```mysql.yaml
source-id: "mysql-src"
from:
  host: "127.0.0.1"
  user: "root"
  password: "<password>"
  port: 3306
```

### DMタスク定義

DMタスク定義では、DMのタスク詳細を記載します。主に、DMのタスクの種類（全量データ移行、継続同期、両方）、タスクの対象とするデータベースやテーブルのフィルタなどを記載します。

今回はemployeesデータベースを全量データ移行と継続の両方で同期(`task-mode: all`)するようにします。

```dm-task.yaml
name: dm-task
task-mode: all

target-database:
  host: "127.0.0.1"
  port: 4000
  user: "root"
  password: "" 

mysql-instances:
  - source-id: "mysql-src"
    block-allow-list:  "ba-rule1"

block-allow-list:
  ba-rule1:
    do-dbs: ["employees"]
```

## DM設定と実行

DMを設定するには、`dm-ctl` コマンドを利用します。TiUPから利用できます。

まずソース定義を使ってソースDBを定義します。

```bash
> tiup dmctl --master-addr=127.0.0.1:8261 operate-source create mysql.yaml

Starting component dmctl: /Users/bohnen/.tiup/components/dmctl/v8.1.0/dmctl/dmctl --master-addr=127.0.0.1:8261 operate-source create mysql.yaml
{
    "result": true,
    "msg": "",
    "sources": [
        {
            "result": true,
            "msg": "",
            "source": "mysql-src",
            "worker": "dm-worker-0"
        }
    ]
}
```

次にタスク定義を使ってタスクを実行します。ワーニングが出ますが、ここでは気にしなくて大丈夫です。

```bash
> tiup dmctl --master-addr 127.0.0.1:8261 start-task dm-task.yaml

Starting component dmctl: /Users/bohnen/.tiup/components/dmctl/v8.1.0/dmctl/dmctl --master-addr 127.0.0.1:8261 start-task dm-task.yaml
{
    "result": true,
    "msg": "",
    "sources": [
        {
            "result": true,
            "msg": "",
            "source": "mysql-src",
            "worker": "dm-worker-0"
        }
    ],
    "checkResult": "fail to check synchronization configuration with type: no errors but some warnings
...
```
タスクの実行状況はステータスコマンドで確認できます。SubTaskStatusのsyncedがtrueになっていれば、同期完了です。

```bash
tiup dmctl --master-addr 127.0.0.1:8261 query-status dm-task

Starting component dmctl: /Users/bohnen/.tiup/components/dmctl/v8.1.0/dmctl/dmctl --master-addr 127.0.0.1:8261 query-status dm-task
{
    "result": true,
    "msg": "",
    "sources": [
        {
            "result": true,
            "msg": "",
            "sourceStatus": {
                "source": "mysql-src",
                "worker": "dm-worker-0",
                "result": null,
                "relayStatus": null
            },
            "subTaskStatus": [
                {
                    "name": "dm-task",
                    "stage": "Running",
                    "unit": "Sync",
                    "result": null,
                    "unresolvedDDLLockID": "",
                    "sync": {
                        "totalEvents": "1",
                        "totalTps": "0",
                        "recentTps": "0",
                        "masterBinlog": "(binlog.000003, 66378917)",
                        "masterBinlogGtid": "",
                        "syncerBinlog": "(binlog.000003, 66378917)",
                        "syncerBinlogGtid": "",
                        "blockingDDLs": [
                        ],
                        "unresolvedGroups": [
                        ],
                        "synced": true,
...
```


## 継続同期

この状態でDMはMySQLのbinlogを監視し、更新をTiDB側に連携します。
試しにMySQL側のdepartmentsテーブルに行を追加してみます。

```sql
-- MYSQLに接続
> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 8.0.40    |
+-----------+

> use employees;
> insert into departments values('d010','Example Department');

> table departments;
+---------+--------------------+
| dept_no | dept_name          |
+---------+--------------------+
| d001    | Marketing          |
| d002    | Finance            |
| d003    | Human Resources    |
| d004    | Production         |
| d005    | Development        |
| d006    | Quality Management |
| d007    | Sales              |
| d008    | Research           |
| d009    | Customer Service   |
| d010    | Example Department | -- 追加した行
+---------+--------------------+
```

TiDB側も確認してみます。

```sql
SELECT VERSION();
+--------------------+
| version()          |
+--------------------+
| 8.0.11-TiDB-v8.1.1 |
+--------------------+

> table departments;
+---------+--------------------+
| dept_no | dept_name          |
+---------+--------------------+
| d001    | Marketing          |
| d002    | Finance            |
| d003    | Human Resources    |
| d004    | Production         |
| d005    | Development        |
| d006    | Quality Management |
| d007    | Sales              |
| d008    | Research           |
| d009    | Customer Service   |
| d010    | Example Department | -- 同期された行
+---------+--------------------+
```

同期されていることが確認できました。

## おわりに

TiDB DMはMySQLからTiDBへのデータ移行を行うツールです。AWS DMSと同様に一括移行と継続同期をサポートし、また今回は触れていませんがmasterノード、workerノードを複数持つことができ耐障害性を高めた構成にすることもできます。

Playground版では簡単に起動できるので設定の確認やお試しにもってこいです。ぜひ活用してみてください。（多分TiDB Cloudに対しても利用できる気がしますが、また別の記事で試してみることにします）