最近のマイブームの1つになっている、全文検索エンジン ElasticSearch に MySQL のデータを取り込んで、MySQL データベースの全文検索エンジンとして ElasticSearch を使う手順の紹介です。
2014070401


まず検索エンジンである ElasticSearch を導入します。日本語形態素解析エンジンである Kuromoji まで含めてのインストール手順を別エントリで紹介しているので、こちらを参照ください:
ElasticSearch に Kuromoji プラグインを導入する


また取り込み先である MySQL サーバーについても環境は構築済みであると仮定します。こちらの構築手順についても、こちらのエントリを参照ください:
CentOS に MySQL をインストール/セットアップする

なお、自分個人的には MySQL ではなく MariaDB を使って同じことをできているので、以下の内容に関しては MariaDB でも同様に可能だと思っています。


さて、ElasticSearch に MySQL データを取り込むための準備として ElasticSearch 自体のインストール後に以下のステップを行う必要があります:
1. (MySQL クライアントと)JDBC ドライバの導入
2. JDBC River プラグインのインストール


最終的には ElasticSearch の River プラグインと呼ばれる拡張機能を使って MySQL からのデータ取り込みを行います。このプラグインの動作に必要な MySQL JDBC ドライバを先にインストールしておく、というステップになります。


まず、これは必須ではありませんが、あると確認に便利なので MySQL のクライアント環境を ElasticSearch サーバー内に構築しておきます。MySQL サーバーに接続する機能があればいいので、MySQL サーバー機能は不要で、クライアント機能だけが必要、ということになります。もし MySQL サーバーと ElasticSearch サーバーが同じサーバーだとすると、既に MySQL クライアント環境は導入済みだと思うので、インストールは不要です。 MySQL クライアントが導入されていない場合は以下のコマンドで MySQL クライアントをインストールします:
# yum install mysql
# vi /etc/my.cnf
  :
(以下の2行を追加)
[mysql]
default-character-set=utf8

MySQL クライアントが導入できた所で、取り込み元の MySQL サーバーへ接続してみます。仮に今回取り込むデータの内容が以下であると仮定します:
 MySQL サーバー: mysql.mylocal.com
 ユーザー名: username
 パスワード: password
 データベース名: mydb
 取り込む内容: samples テーブル


実際に MySQL クライアントで目的のデータベースにアクセスしてみます。ここまでが出来るようであればファイアウォールなども含めて接続準備ができているといえます:
# mysql -h mysql.mylocal.com -u username -ppassword mydb
> select * from samples;
:
:
(samples テーブルの内容)
:
: > quit


次に MySQL サーバーへ Java 環境から接続するための JDBC ドライバー(MySQL Connector/J)を導入します。ドライバー自体はこちらのサイトからダウンロードできます:
MySQL :: Download Connector/J 


ダウンロードしたファイルを展開して JAR ファイルを取り出し、/usr/share/java にコピーします:
# unzip mysql-connector-java-5.1.30.zip
# cd mysql-connector-java-5.1.30
# cp mysql-connector-java-5.1.30-bin.jar /usr/share/java

環境変数 CLASSPATH に、この JAR ファイルを追加します:
# vi /etc/bashrc
  :
(以下の1行を追加)
export CLASSPATH=$CLASSPATH:/usr/share/java/mysql-connector-java-5.1.30-bin.jar
 

# source /etc/bashrc

JDBC ドライバの準備が出来た所で River プラグインをインストールします。リポジトリを確認したところ、2014/07/01 時点での最新バージョンは 1.2.1.1 だったので、このバージョンを指定して導入します:
# /usr/share/elasticsearch/bin/plugin --install jdbc --url http://xbib.org/repository/org/xbib/elasticsearch/plugin/elasticsearch-river-jdbc/1.2.1.1/elasticsearch-river-jdbc-1.2.1.1-plugin.zip
# cp /usr/share/java/mysql-connector-java-5.1.30-bin.jar /usr/share/elasticsearch/plugins/jdbc/

これで必要なソフトウェアは揃いました。では実際に MySQL からデータを取り込んでみましょう

まず ElasticSearch 側に kuromoji を使った検索インデックス(kuromoji_sample)を作成します。インデックスの作成については別エントリでも紹介しましたが、まだこの内容を実行していない場合は以下のコマンドを実行します(コマンドは黒字部分で、青字はレスポンスを表しています):
# curl -XPUT http://localhost:9200/kuromoji_sample -d '{ "index": { "analysis": { "tokenizer": { "kuromoji_user_dict" : { "type":"kuromoji_tokenizer" } }, "analyzer": { "analyzer": { "type":"custom", "tokenizer": "kuromoji_user_dict" } } } } }'
{"acknowledged":true}

次に River を使って、作成した kuromoji_sample インデックスに MySQL データベースサーバーからデータを取り込みます:
# curl -XPUT http://localhost:9200/_river/my_jdbc_river/_meta -d '{ "type": "jdbc", "jdbc": { "url": "jdbc:mysql://mysql.mylocal.com:3306/mydb", "user": "username", "password": "password", "sql": "select * from samples", "index": "kuromoji_sample", "type": "samples" } }'
{"_index": "_river", "_type": "my_jdbc_river", "_id": "_meta", "_version": 1, "created": true}

上記入力パラメータ(JSON)の中で選択(select)の SQL を発行しています。この SQL の実行結果が ElasticSearch に取り込まれることになります。

取り込みができたら、最後に検索してみます。この例では取り込んだデータの name フィールドに「ほげほげ」が含まれているデータを検索しています:
# curl -XPOST http://localhost:9200/kuromoji_sample/_search?pretty -d '{ "query": { "query_string": { "query": "name:ほげほげ" } } }'
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 29,
    "max_score" : 3.687642,
    "hits" : [ {
      "_index" : "kuromoji_sample",
      "_id" : "XXXXXXXXXXXXXX",
      "_score" : 3.687642,
      "_source":{"id":"1234","name":"ほげほげ"}
    }, {
      "_index" : "kuromoji_sample",
        :
    } ]
  }
}

こんな感じで実現できました。

MySQL の like 節を使った単純検索をしていた頃と比べると、以前は localhost 内の MySQL に対して検索していたのでネットワークによる遅延はほとんどなかったはずで、今回作ってみた環境はリモートの ElasticSearch 環境にアクセスしているので、ネットワークの遅延影響がでるはずです。

にも関わらず、検索パフォーマンスは 10 倍程度になりました。これはでかい!

ElasticSearch の検索パターンやその API 実行方法についてはいずれまたプログに書く予定です。