Azure Database Migration Serviceを利用したデータ移行の準備作業について

# はじめに
仮想マシン上にホストしていたMySQLをAzure Databaseに移行する機会があったのですが、準備作業に手間がかかったので、ToDoとしてまとめておきます。

# 前提
* ソース
Ubuntu 18.04
MySQL 5.7

* ターゲット
Azure Database for MySQL (ver 5.7)
SKU Standard
Private Link & Private Endpoint

# 構成
設定変更が必要だったのですが、要件の都合上ダウンタイムが許容されないため移行用のソースDB（レプリカ）を作成し、データ同期を行いました。

![test.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23761/c0712408-2e30-02eb-efb7-7e78dc54451c.png)



# やること
詳細は[こちら](https://docs.microsoft.com/ja-jp/azure/dms/tutorial-mysql-azure-mysql-online#prerequisites)を参照ください。
以降ポイント抑えて紹介します。

* 設定変更  
次の構成を使用して、ソースDBの my.ini (Windows) または my.cnf (Unix) ファイルのバイナリ ログを有効にします。（要再起動）

```
[mysqld]
...
binlog_format = row
log_slave_updates = 1
...
```

* 移行用ユーザを作成  
ソースDBに対し移行用ユーザを用意します。※ユーザ名とパスワードは適宜変更ください

```
CREATE USER 'dms'@'%' IDENTIFIED BY 'secret';
GRANT REPLICATION SLAVE ON *.* TO 'dms'@'%';
GRANT REPLICATION CLIENT ON *.* TO 'dms'@'%';
GRANT ALL PRIVILEGES ON <migration db>.* TO 'dms'@'%';
FLUSH PRIVILEGES;
```

* ターゲットDBインスタンスの作成  
Azure Databaseを作成します。詳細は[こちら](https://docs.microsoft.com/ja-jp/azure/mysql/quickstart-create-mysql-server-database-using-azure-portal)を参照ください。
作成する際、一部のパラメータの変更します。

```
innodb_lock_wait_timeout = 1073741824
```

* Private Link&Private Endpoint設定
こちら必須ではないですが、Azure Databaseに対しVNet内からのアクセスしか許容しないようなセキュアな要件が必要なケースは推奨です。詳細は[こちら](https://docs.microsoft.com/ja-jp/azure/mysql/concepts-data-access-security-private-link#configure-private-link-for-azure-database-for-mysql)を参照し作成ください。

* 外部キー解除 
ソースDB内に外部キーが存在しているかチェックします。
クエリ後半の`...KCU.REFERENCED_TABLE_SCHEMA = 'SchemaName'...`のSchemaNameは適宜DB名に変更ください。 
存在しているようでしたら抽出結果のdropクエリを発行し、解除ください。
移行完了後、再度外部キーを設定する時のために設定(Add)クエリを控えるように。

```
SET group_concat_max_len = 8192;
    SELECT SchemaName, GROUP_CONCAT(DropQuery SEPARATOR ';\n') as DropQuery, GROUP_CONCAT(AddQuery SEPARATOR ';\n') as AddQuery
    FROM
    (SELECT
    KCU.REFERENCED_TABLE_SCHEMA as SchemaName,
    KCU.TABLE_NAME,
    KCU.COLUMN_NAME,
    CONCAT('ALTER TABLE ', KCU.TABLE_NAME, ' DROP FOREIGN KEY ', KCU.CONSTRAINT_NAME) AS DropQuery,
    CONCAT('ALTER TABLE ', KCU.TABLE_NAME, ' ADD CONSTRAINT ', KCU.CONSTRAINT_NAME, ' FOREIGN KEY (`', KCU.COLUMN_NAME, '`) REFERENCES `', KCU.REFERENCED_TABLE_NAME, '` (`', KCU.REFERENCED_COLUMN_NAME, '`) ON UPDATE ',RC.UPDATE_RULE, ' ON DELETE ',RC.DELETE_RULE) AS AddQuery
    FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU, information_schema.REFERENTIAL_CONSTRAINTS RC
    WHERE
      KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
      AND KCU.REFERENCED_TABLE_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA
  AND KCU.REFERENCED_TABLE_SCHEMA = 'SchemaName') Queries
  GROUP BY SchemaName;
```

* トリガー解除  
移行時のデータ整合性担保の為に一時的にトリガーを解除します。表示された結果内容を発行します。

```
SELECT Concat('DROP TRIGGER ', Trigger_Name, ';') FROM  information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = 'your_schema';
```

* LOBのデータ型確認  
`mediumtext / longtext / blob /mediumblob / longblob`型のカラムを探し、32KB(32768byte)を超えていないか確認です。超えている場合、ターゲットDBにおいて切り捨てられるため、ソースDBに対し、下記クエリでデータの長さを確認します。

```
SELECT max(length(foo)) as LEN from hoge;
```
移行プロジェクトを作成する際、移行ウィザードの[移行の設定の構成]のステップでLOBに関する設定変更が可能ですので、こちらでLOBのデータ長を入力ください。


* スキーマの移行  
ソースDBからDBスキーマをエクスポートします。

```
mysqldump -h [servername] -u [username] -p[password] --databases [db name] --no-data > [schema file path]
ex) mysqldump -h 192.168.0.4 -u foo -p --databases soruce --no-data > ~/import.dump
```
エクスポートしたスキーマ内に `DEFINER` が含まれているかを `find` コマンド等でチェックし、存在する場合は除外します。

```
ex) sed -i -e 's/DEFINER=`root`@`localhost`//g' [schema file path]
```
ターゲットDBへインポート

```
mysql -h [servername] -u [username] -p[password] [database] < [schema file path]
ex) mysql -h hoge.mysql.database.azure.com -u foo -p target < ~/import.dump
```


# その他
* ターゲットDBの価格レベル（インスタンスサイズ）の変更は可能か？
可能でした。
当初コスト抑制のため、最小スペック（GP 2vCore）でターゲットを立てましたが、本番稼働ではメモリ最適化タイプへの価格レベル変更が必要でした。
切り替え前に価格レベルの変更を行ってみましたが、特に問題なくできました。
DMS側で何かエラーが発生していた形跡はなかったと記憶してます。


# 参考
https://docs.microsoft.com/ja-jp/azure/dms/tutorial-mysql-azure-mysql-online
https://docs.microsoft.com/ja-jp/azure/dms/known-issues-troubleshooting-dms