1. DTSとは
DTS(Data Transmission Service)は、リレーショナルデータベース、NoSQL、OLAPなど様々なデータベース間でのデータ移行やリアルタイムデータ同期を実現するAlibaba Cloudのマネージドサービスです。
AWSのDMS(Database Migration Service)に相当しますが、DTSはより多様な機能を提供します。
DTSは、次のような主要機能を提供します。
-
Data migration
: データソースにあるスキーマや既存のデータやキャプチャされた変更などをターゲットデータベースへ移行する機能- オンプレや他社のクラウド上のデータベースをAlibaba Cloudへ移行する時に利用
- データ移行タスクは、作業完了後に削除することが一般的です。これは、一度きりのデータ移行に最適なアプローチです。
-
Data synchronization
:リアルタイムにdata sourcesを双方向
同期する機能、以下のシナリオで利用されことが多いです。(一回きりのタスクではなく、常時稼働なタスク)- DRサイトの構築(マインのサイトから継続的にデータをDRサイトへリアルタイム同期)
- multi-site(DR用ではなく、2つのサイトは同時にサービスを提供し、end userからのリクエストは自動的に地理的に近いサイトに振り分けられる)間のデータのリアルタイム同期
- 業務用データベース(OLTPのデータベース)からデータをdata warehouseへ同期
-
Change tracking
: データソースのdataの変更をキャプチャし、publisher/subscriber streamとしてメッセージを第三者に送る- 複数のsubscriberに同時にメッセージを送れるのは特徴です。(AWSのSNSに似ているところがあります。)
今回はData migration
を中心として、説明します。
2. Data migration
の仕組み
-
ポイントとしては、初期データ(既存データ)の同期と変更されたデータの同期を別々のサブタスクとして実行します。厳密に言うと、「スキーマのマイグレーション」という選択肢もあります
-
実行順序としては、スキーマのマイグレーション → 初期データのマイグレーション → 増分データのマイグレーションという順に行います
-
3つのサブタスクは単独で(つまり、スキーマのみ同期、または初期データのみ同期、または増分データのみの同期)実行も可能ですが、一般的には3つすべてを選択することをおすすめします
初期データのみを同期し、変更されたデータを同期しない方法を選ぶと、データ移行時にソースデータベース側のアプリケーションを完全に停止し、データの変更を行わないようにする必要があります。データ量が多い場合、移行には数十時間かかることがあり、その間はサービスが完全に停止してしまいます。そのため、可能な限り「増分データの同期」も選択することをおすすめします。
- データをそのまま同期するのではなく、DTSの
ETL機能
を通じてデータをフィルタリングや加工を行い、その後ターゲットデータベースに書き込むことも可能です。
- フィルタリング:例えば、一定の条件を満たすデータしか同期しない
- データ検証:データ同期プロセスにおけるレコードの欠損、特定カラムの値の漏れ、またはスキーマ(カラム)に関する問題を人手で確認することは非常に困難です。このため、データ検証(data verification)機能が役立ちます。この機能を活用すると、ソースデータベースとターゲットデータベースの各行のhash値を計算し、両者が一致しているかどうかをチェックします。全てが一致していれば、ソースデータベースとターゲットデータベースのデータが完全に同じであることが確認できます。
- 単純に両データベースの行数が一致しているかどうかの検証も可能です。ソースデータベースとターゲットデータベースが異なるエンジンを使用している場合(例:OracleからPolarDB for Oracleに移行)、データの一部が変換される可能性があるため、行数の一致のみを確認することが推奨されます。
- 料金:
- 行ごとのhash値の比較:有料
- 行数のみ検証:無料
- 既存の全文データおよび差分データの検証:有料
- schema verification: 無料
3. データ移行の流れ
- DTSタスクを作成し、データをリアルタイムにsource databaseからtarget databaseに同期
- データのマイグレーションが完了したら、データ検証を行い、欠損や漏れがないかを自動チェック
- サービス(Application)を一時に停止し、source databaseにデータを書き込まないようにする
- 増分データの移行と増分データの検証を除くすべてが完了し、さらに増分データの遅延が約1秒程度になった場合、すべての同期対象が同期されたとみなします。
- ソースデータベースにログインし、セッション情報を確認し、新しいセッションが書き込み操作を実行していないことを確認します
MySQLの場合: show processlist;
- アプリケーション側のdatabase接続endpointをsource databaseからtarget databaseに変更
- 新データベースでアプリケーションの動作確認
- 全て問題ないことが確認できた後、DTS taskの削除とsource databaseの停止などを行う
4. Data migration
の利用パターン
-
Alibaba Cloud内部のマネージドデータベース間のデータ同期
例えば RDS → PolarDB あるいは PolarDB → RDS
-
Alibaba Cloud以外のクラウド同士のデータベースのデータ同期
今回は利用パターンの中の一つ目の Alibaba Cloud内部のマネージドデータベース間のデータ同期について解説します。
5. RDSからPolarDBへのデータマイグレーション
5.1 前提
- RDS MySQLとPolarDB MySQLのバージョンを統一することが推奨されます。これにより、互換性の問題を防ぎます
- region, VPC, Alibaba Accountなどは同じにする必要がありません
5.2 事前準備
- RDS
-
ローカルPCのmysql clientからRDSとPolarDBにアクセスさせるため、 public endpointを作成しておく
-
データの準備
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 | +----+-----------+----------+-------------------+---------------------+
- PolarDB:
-
データベースの中身を確認(
dts_test
というデータベースが存在していない)mysql> show databases; +--------------------+ | Database | +--------------------+ | __recycle_bin__ | | information_schema | | mysql | | performance_schema | | sys | +--------------------+
5.3 DTSタスク作成
-
Data Migration
タスクを作成
- source databaseのRDSとtarget databaseのPolarDBの情報を入力し、DTSからそれぞれ接続できるかどうかを確認(
Test Connectivity and Proceed
ボタンを押下)
- デフォルト設定では、既存データの全量移行(
Full Data Migration
)のみが選択されています。しかし、スキーマ情報(テーブル構成)と差分データの同期も行いたい場合は、スキーマ移行(Schema Migration
)と増分データ移行(Incremental Data Migration
)も追加で選択することをお勧めします。
全量データ移行のみを選択した場合、サービスの切り替え前には、すべてのデータが同期されるまでサービスを完全に停止する必要があります。サービスのダウンタイムを最小限に抑えたい場合は、スキーマ移行、全量データ移行、増分データ移行の3つを選択することが推奨されます。
- DTSインスタンスのクラスを選択
DTSインスタンスのクラスは増分データ同期する時の最大RPS(record per second)
に影響します。
例えばソースデータベースからターゲットテーブルに毎秒5000行のデータが増分同期される場合、RPSは5000です。
今回はテストのため、一番小さいsmall
インスタンスタイプを選択します。
-
デフォルトでは終了した子タスクが表示されない。全体のプロセスを確認するため、
Hide Completed Modules
のチェックを外します。
-
赤い部分はすでに完了している子タスクであり、青い子タスクは実行中になります。
増分データはいつでも来る可能性があるため、それに対応する子タスクは基本的に常時実行状態を維持します。
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 差分データの同期
-
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 | +----+-----------+----------+-------------------+---------------------+
-
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 | +----+-----------+----------+-------------------+---------------------+
-
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 | +----+-----------+----------+-------------------+---------------------+
-
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 | +----+---------+--------+
-
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 一致しているケース
task起動した最初はmyguests
テーブルに2件のデータしかなかったため、既存データの部分は2件がチェックされ、一致していないレコードがありません。(一致していないレコードがある場合はActions
欄に内容が表示されます)
tableやviewやFunctionなどがチェックの対象になります。今回はテーブルしかありません。しかもチェックした結果は一致していないテーブルがありません。
-
mygusts
テーブル: insert一件、update一件、delete一件 合計3件のデータがチャックされて、一致していないレコードが存在していません。 -
items
テーブル: insert 二件のデータがチェックされて、一致していないレコードが存在していません。
5.4.3.2 一致していないケース
上記では、全てが一致する状況を説明しました。次に、データが一致しない場合、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
)させる
-
items
テーブルに一件一致していないデータが存在している。view report
リンクをクリック
-
id
=1のデータのcategory
というカラムにもとデータがNULL
であり、targetデータはPC
になっていることが分かります。
- 再度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
)させる。すべてのデータが一致になりました。
5.4.4 ETLでデータを加工してからターゲットデータベースに書き込む
5.4.4.1 ETLの動き
今度はソースデータベースのitem
テーブルのcategory
カラムにNULLをother
という文字列に変更したうえで、ターゲットデータベースに書き込みます。
e_if(op_is_null(`category`), e_set(`category`,"other"))
- ソースデータベースに新しいレコードを作成します
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 データ検証
やはりETLでデータ変換された場合は、hash値での比較方法はかならず不一致になってしまうため、hash値の比較の代わりに、行数の一致のみを確認することがおすすめです。
-
Full Data Verification
とIncremental Data Verification
の子タスクを削除して、新しい子タスクを作り直します
-
データ検証の種類を選択し、「行数を検証」を選択
増分データは常に増加しているため、増分データの行数が一致するかどうかを検証することは不可能(そもそも意味がない)です。ETLでデータを加工する場合、行数の検証に頼るしかありません。しかし、増分データの行数が一致するかどうかを確認すること自体が元々不可能であるため、増分データの検証は省略すべきです。省略しない場合、増分データの検証はハッシュ値の比較になり、結果は必ず不一致となってしまいます。
行数一致していないパターンの確認:
-
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 | +----+------------+--------+----------+
-
source databaseに3件のデータがあり、target databaseに2件のデータしかなくて、1件の差があることになります
6. まとめ
- DTSを使用する際、既存データと増分データの両方を選択することで、サービスのダウンタイムを最小化しつつ効率的にデータ移行が可能です。
- 増分データを選択した場合、ソースデータベースで新規に作成されたデータ(
INSERT
)、更新されたデータ(UPDATE
)、削除されたデータ(DELETE
)、さらにはCREATE TABLE
やALTER TABLE
やCREATE INDEX
などのDDL文も自動的に同期されます。 - DTSのdata verification機能を利用すると、データの漏れや差異が自動的に検出されるため、非常に便利です。
- DTSの
ETL機能
を利用すると、一部のデータをフィルタリングや加工を行い、その後ターゲットデータベースに書き込むことも可能です。