MySQLからElasticsearchにデータをインポートする
背景
アプリケーションを作る中で全文検索やらファセットが必要になったので、DBのデータをElasticsearchにインポートしようと思い立ちました。その際に試行錯誤した内容のまとめです。インストール周りの作業は基本的にansibleを使ってます。
方法の検討
2つの方法を試してみました。
(正確には片方を苦労してやったあとでもう片方を見つけて泣く泣くやり直した)
方法1. JDBC importer
The Java Database Connection (JDBC) importer allows to fetch data from JDBC sources for indexing into Elasticsearch.
Java DataBase Connectionに準拠しているソースのデータをインポートできる。業務のSolrにもこれ使ってた気がするし、スター1000個もついてるし安心!と思いました。
方法2. Embulk
Embulk is a parallel bulk data loader that helps data transfer between various storages, databases, NoSQL and cloud services.
Embulk supports plugins to add functions. You can share the plugins to keep your custom scripts readable, maintainable, and reusable.
OSSのデータ転送ソフトウェア。fluentdのバッチ版と言えばわかりやすいらしいけど、実はfluentdをまともに使ったことがなかった。データ転送のインプットとアウトプットがプラグインになっていて、input-mysqlとoutput-elasticsearchを使えば今回の目的は達成できそう。
結論
どちらの方法でも今回の作業は可能。ただ、Embulkの方はインプットとアウトプットを他のデータストアに変えても使えそうだったので、つぶしが効くというメリットでEmbulkを使うことにしました。
ただせっかく両方やったので、作業内容と注意点は両方とも書いてみます。
作業
前提
$ mysql --version mysql Ver 14.14 Distrib 5.1.73, for redhat-linux-gnu (x86_64) using readline 5.1 $ curl localhost:9200 { "status" : 200, "name" : "Heimdall", "cluster_name" : "elasticsearch", "version" : { "number" : "1.7.1", "build_hash" : "b88f43fc40b0bcd7f173a1f9ee2e97816de80b19", "build_timestamp" : "2015-07-29T09:54:16Z", "build_snapshot" : false, "lucene_version" : "4.10.4" }, "tagline" : "You Know, for Search" }
JDBC importerでインポート
参考にさせて頂いたページ
手順
JDBCツールのインストール
ansible: jdbcロールのtasks
- name: download jdbc get_url: url="{{jdbc_url}}" dest=/tmp - name: unzip jdbc unarchive: src=/tmp/{{jdbc_package}}.zip dest=/usr/local/src copy=no
ansible: jdbcロールのvars
jdbc_url: http://xbib.org/repository/org/xbib/elasticsearch/importer/elasticsearch-jdbc/1.7.1.0/elasticsearch-jdbc-1.7.1.0-dist.zip jdbc_package: elasticsearch-jdbc-1.7.1.0-dist
参考ページにも書かれていますが、jdbcのバージョンは完全にElasticsearchと合わせてます。これを守っていないと後の作業で詰まります。
関連ソース
Cannot connect to remote elasticsearch · Issue #567 · jprante/elasticsearch-jdbc · GitHub
スクリプトの作成
/usr/local/src/elasticsearch-jdbc-1.7.1.0/bin
以下にmysql-delete-document.sh
というスクリプトがあるので、それをコピーして書き換えてください。
$ cd /usr/local/src/elasticsearch-jdbc-1.7.1.0/bin $ cp mysql-delete-document.sh mysql2es.sh
以下、DB名、ユーザ名、パスワード、テーブル名、カラムは自身のものに置き換えてください。
# mysql2es.sh #!/bin/sh DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" bin=${DIR}/../bin lib=${DIR}/../lib echo '{ "type" : "jdbc", "jdbc" : { "url" : "jdbc:mysql://localhost:3306/$DATABASE", "user" : "$USER", "password" : "$PASSWORD", "sql" : "SELECT id AS _id, title, url FROM $TABLE" } } ' | java \ -cp "${lib}/*" \ -Dlog4j.configurationFile=${bin}/log4j2.xml \ org.xbib.tools.Runner \ org.xbib.tools.JDBCImporter
スクリプトの実行
素直に実行すればOKです。事前にElasticsearchを起動させておくのを忘れずに…。
$ cd /usr/local/src/elasticsearch-jdbc-1.7.1.0/bin $ ./mysql2es.sh
結果の確認は省略。Elasticsearchにクエリを投げれば結果が返ってくるはずです。なお、今回は事前にマッピングを作成していないので、Elasticsearch側で動的に作成されます。文字列にアナライザをかけたりごにょごにょしたい場合は、先にやっておけばいいと思います。
Embulkでインポート
手順
Embulkのインストール
ダウンロード、コマンドへのシンボリックリンク作成、プラグインのインストールを行います。
自分のansible力が低いのと今回あまり時間がないのとで、shellモジュール使ってたり書き方が甘々なのはご容赦ください。varsの使い方とかももっと工夫できるはず…。
ansible: embulkロールのtasks
- name: download embulk get_url: url="{{embulk_url}}" dest=/usr/local/src - name: make symlink for embulk file: src=/usr/local/src/{{embulk_package}} dest=/usr/local/bin/embulk state=link - name: download plugins for embulk shell: /usr/local/bin/embulk gem install {{item}} with_items: - embulk-input-mysql - embulk-output-elasticsearch
ansible: embulkロールのvars
embulk_url: http://dl.embulk.org/embulk-latest.jar embulk_package: embulk-0.7.4.jar
確認
$ /usr/local/bin/embulk --version embulk 0.7.4
上記のように表示されればインストールは完了です。自分がこれをやったとき、バージョンの表示だけで1分以上かかって絶望したんですが、JDKをopenjdk1.8からoracleのjdk1.8に変更したら10倍くらいは高速になりました。一体なんだったのか…
参考
ansibleを学ぶ:vol02:Oracle JDK1.8のインストール、ユーザパスワード設定等 - 文系プログラマによるTIPSブログ
設定ファイルの作成
以下、変数部分とDBのカラムは自身のものに置き換えてください。
# config.yml in: type: mysql host: localhost port: 3306 user: $USER password: $PASSWORD database: $DATABASE query: | SELECT id, title, url FROM $TABLE out: type: elasticsearch index: $INDEX index_type: $INDEX_TYPE nodes: - host: localhost
ややこしいんですが、yaml内でキーになっているtype
はEmbulkのin
とout
のそれぞれで使われるプラグインを示しています。一方、index_type
はElasticsearchでインデックスに紐づく単位である「タイプ」を示しているようです。
変数の利用
Configuration — Embulk 0.7 documentation
Embulkのバージョン0.6.22以上では、RubyのLiquidテンプレートを使って変数が使えるようです。このテンプレートエンジンは初めて知りましたが、Smartyライクらしいので、twigやらjinja2を使ったことがある人はすぐ親しめると思います。
ファイル名のyml
をyml.liquid
に変更して、中に変数を埋め込むだけで良いみたいです。DBのパスワードとかは変数にしておいて、サーバの環境変数から取り出すようにすれば便利そうですね。
Embulkの実行
# 設定ファイルの書き方が正しいかどうかの確認 $ /usr/local/bin/embulk preview /path/to/config.yml # 実行 $ /usr/local/bin/embulk run /path/to/config.yml
結果の確認は省略。Elasticsearchにクエリを投げれば結果が返ってくるはずです。なお、今回は事前にマッピングを作成していないので、Elasticsearch側で動的に作成されます。文字列にアナライザをかけたりごにょごにょしたい場合は、先にやっておけばいいと思います。
まとめ
自分はEmbulkを選択したんですが、ただデータを入れる分にはどちらを使っても困らないと思います。Elasticsearch自体が流行ってるので、何するにしろググれば関連記事は出てくるはず。
ただ、AWSやGCPの利用が一般的になったせいでデータの移動タスクがちょこちょこ発生しそうなので、Embulkがさらにメジャーになればよりエコで楽になる気がしました。