本記事はZOZOテクノロジーズ アドベントカレンダー#2の24日目の記事です。
概要
ZOZOTOWNのリプレイスでは Embulk を使いDBのデータ移行を行っております。
Embulk とはデータ転送のためのツールで、plugin (input、output、filter) を組み合わせて様々なデータを意のままに転送することができます。
今回、MongoDB → MySQL へのデータ移行を行ったのですが、
あまり情報が出回ってなかったので参考になればと思い、手順を紹介したいと思います。
TL;DR;
-
embulk-input-mongodb、embulk-output-mysql
を使う - filter を使いデータ加工を行う必要がある
- カラム展開は expand_json を使う
- NULLデータの default 値は column で設定
移行要件
下記の要件でデータ移行を行いました。
- MongoDB 2.6 → mysql 5.7 へデータ移行
- MongoDB のデータはフィールド数がまちまち
- MySQLで NOT NULL としたいカラムに対し、MongoDB で Key が存在しないデータの場合デフォルト値を指定して移行
検証用環境構築
簡単に動作確認ができるよう、docker-compose で検証用環境をサクッと作ってしまいましょう。
手順だけ確認したいよ、という方はこちらは読み飛ばして下さい。
version: "3.7"
networks:
backend:
services:
mongodb:
image: mongo:2.6
container_name: mongodb
networks:
- backend
ports:
- 27018:27017
mysql:
image: mysql:5.7
container_name: mysql
environment:
- MYSQL_DATABASE=test
- MYSQL_ROOT_PASSWORD=hogehoge
networks:
- backend
ports:
- 3307:3306
上記 docker-compose.yml
を作成後、下記コマンドを実行ください。
$ docker-compose up -d
$ docker-compose exec mongodb mongo test
(mongo)> db.Users.insert([
{"id": 1, "name": "hoge", "email": "hoge@example.com", "age": 20},
{"id": 2, "name": "fuga", "email": "fuga@example.com", "age": 25},
{"id": 3, "name": "foo", "age": 30}
])
下記の様なデータが格納されていれば準備 ok です。
> db.Users.find()
{ "_id" : ObjectId("5fe2679a62a2dec6c0f0751b"), "id" : 1, "name" : "hoge", "email" : "hoge@example.com", "age" : 20 }
{ "_id" : ObjectId("5fe2679a62a2dec6c0f0751c"), "id" : 2, "name" : "fuga", "email" : "fuga@example.com", "age" : 25 }
{ "_id" : ObjectId("5fe2679a62a2dec6c0f0751d"), "id" : 3, "name" : "foo", "age" : 30 }
MongoDB → MySQL データ移行
input-mongodb、output-mysql plugin
input : embulk-input-mongodb
output : embulk-output-mysql
を使うことで、MongoDB → MySQL へデータ移行ができます。
Embulk の YAML は下記の様になります。
in:
type: mongodb
hosts:
- {host: 127.0.0.1, port: 27018}
database: test
collection: "Users"
out:
type: mysql
host: 127.0.0.1
port: 3307
user: root
password: hogehoge
database: test
table: users
mode: replace
embulk run config.yml
を実行すると MySQL users テーブルへ下記データが移行出来ます。
$ mysql -u root -phogehoge -P 3307 -h 127.0.0.1 test -e "select * from users;"
+-------------------------------------------------------------------------------------------------+
| record |
+-------------------------------------------------------------------------------------------------+
| {"_id":"5fe2679a62a2dec6c0f0751b","id":1.0,"name":"hoge","email":"hoge@example.com","age":20.0} |
| {"_id":"5fe2679a62a2dec6c0f0751c","id":2.0,"name":"fuga","email":"fuga@example.com","age":25.0} |
| {"_id":"5fe2679a62a2dec6c0f0751d","id":3.0,"name":"foo","age":30.0} |
+-------------------------------------------------------------------------------------------------+
MongoDB のデータを MySQL へ移行できました。
しかし、record カラムに json 構造がそのままデータが入っている状態です。
そこで、フィルタを使ってデータの加工を行っていきます。
expand_json filter を使い json をカラムに展開
filter : expand_json
を使います。
embulk-input-mongodb では、デフォルトでoutput されるカラム名は record
となるため、json_column_name
を record
とします。
in:
type: mongodb
hosts:
- {host: 127.0.0.1, port: 27018}
database: test
collection: "Users"
filters:
- type: expand_json
json_column_name: record
expanded_columns:
- {name: id, type: long}
- {name: name, type: string}
- {name: email, type: string}
- {name: age, type: long}
out:
type: mysql
host: 127.0.0.1
port: 3307
user: root
password: hogehoge
database: test
table: users
mode: replace
embulk run config.yml
を実行すると MySQL users テーブルへ下記データが移行出来ます。
$ mysql -u root -phogehoge -P 3307 -h 127.0.0.1 test -e "select * from users;"
+------+------+------------------+------+
| id | name | email | age |
+------+------+------------------+------+
| 1 | hoge | hoge@example.com | 20 |
| 2 | fuga | fuga@example.com | 25 |
| 3 | foo | NULL | 30 |
+------+------+------------------+------+
json をカラムに展開しつつデータ移行できました。
MongoDB で email の Key を持たないデータは、email が NULL となっています。
email カラムを NOT NULL としたい場合この状態は望ましくありません。
こちらもフィルターを使うことで、NULLではなく ''(空)
など default 値を設定することが出来ます。
column filter を使い default 値を指定
filter : column
を使います。
in:
type: mongodb
hosts:
- {host: 127.0.0.1, port: 27018}
database: test
collection: "Users"
filters:
- type: expand_json
json_column_name: record
expanded_columns:
- {name: id, type: long}
- {name: name, type: string}
- {name: email, type: string}
- {name: age, type: long}
- type: column
columns:
- {name: id}
- {name: name}
- {name: email, default: ''}
- {name: age}
out:
type: mysql
host: 127.0.0.1
port: 3307
user: root
password: hogehoge
database: test
table: users
mode: replace
embulk run config.yml
を実行すると MySQL users テーブルへ下記データが移行出来ます。
+------+------+------------------+------+
| id | name | email | age |
+------+------+------------------+------+
| 1 | hoge | hoge@example.com | 20 |
| 2 | fuga | fuga@example.com | 25 |
| 3 | foo | | 30 |
+------+------+------------------+------+
email カラムを ''(空)
としてデータ移行できました。
以上で要件通りデータの移行が行えました。
まとめ
embulk-input-mongodb に filter を組み合わせることで、要件通り MongoDB → MySQL のデータ移行ができました。
Embulk には bult-in を含め多数の plugin が存在しますので、様々な要件に柔軟に対応することができます。
(弊社では Sonots、Civitaspo などが様々な plugin を公開しており、今回使用した filter もお二人作のものです。)
データ移行を伴うリプレイス時など、
ご興味ありましたら是非 Embulk を使い簡単にデータ移行を行ってみて下さい。
ご一読頂きありがとうございました。