5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ZOZOテクノロジーズ #2Advent Calendar 2020

Day 24

EmbulkでMongoDB→MySQLへデータ移行してみた

Posted at

本記事はZOZOテクノロジーズ アドベントカレンダー#2の24日目の記事です。

概要

ZOZOTOWNのリプレイスでは Embulk を使いDBのデータ移行を行っております。
Embulk とはデータ転送のためのツールで、plugin (input、output、filter) を組み合わせて様々なデータを意のままに転送することができます。

今回、MongoDB → MySQL へのデータ移行を行ったのですが、
あまり情報が出回ってなかったので参考になればと思い、手順を紹介したいと思います。

TL;DR;

移行要件

下記の要件でデータ移行を行いました。

  • MongoDB 2.6 → mysql 5.7 へデータ移行
  • MongoDB のデータはフィールド数がまちまち
  • MySQLで NOT NULL としたいカラムに対し、MongoDB で Key が存在しないデータの場合デフォルト値を指定して移行

検証用環境構築

簡単に動作確認ができるよう、docker-compose で検証用環境をサクッと作ってしまいましょう。
手順だけ確認したいよ、という方はこちらは読み飛ばして下さい。

docker-compose.yml
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 は下記の様になります。

config.yml
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_namerecord とします。

config.yml
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
を使います。

config.yml
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 が存在しますので、様々な要件に柔軟に対応することができます。
(弊社では SonotsCivitaspo などが様々な plugin を公開しており、今回使用した filter もお二人作のものです。)

データ移行を伴うリプレイス時など、
ご興味ありましたら是非 Embulk を使い簡単にデータ移行を行ってみて下さい。

ご一読頂きありがとうございました。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?