SlideShare a Scribd company logo
07th
Feb, 2014

MySQLユーザ視点での
小さく始めるElasticsearch
Kentaro Yoshida in 第3回 Elasticsearch勉強会

page 1
1. はじめに
2. 自己紹介
3. 今回のテーマ

本日の流れ

4. Yamabikoの紹介
5. 検索ことはじめ
6. Elasticsearch雑感
7. まとめ

page 2
1. はじめに

page 3
こんなお悩みを抱えていませんか?

page 4
MySQLを利用している

page 5
だけれども、

page 6
検索漏れが少ない日本語全文検索
“Kuromoji”を使いたい!
Searchモード・Extendedモードが秀逸

page 7
つまり

page 8
“Solr” or “Elasticsearch” が必要

page 9
APIをRESTfulに扱える
“Elasticsearch” がアツい

page 10
【課題】
異種RDB間のデータ同期

page 11
2. 自己紹介

page 12
自己紹介
• よしけんさん
• (株)リブセンス
• Web系インフラの

研究開発エンジニア
• Elasticsearch歴:

2013年 初夏∼
好きなプロダクト

page 13
お知らせ
MySQLユーザ視点での小さく始めるElasticsearch
3.今回のテーマ

page 16
今回のテーマ
実データを用いて手軽にElasticsearchと連携した検索を行いたい
Elasticsearchをスモールスタートで使い始めたい
既存プログラムの更新系処理に触れずに小さく始めたい
メインRDBはMySQLではあるが、検索のみElasticsearchを使う構成
Amazon RDS for MySQLにも応用できる手離れの良い構成にしたい
MySQLサーバの管理無しに冗長化構成を実現できる (Multi-AZ)

page 17
MySQLのレコードを
Elasticsearchへ同期したい

page 18
つまり
異種RDB間のデータ同期

page 19
既存製品はElasticsearch非対応
Attunity Replicate / Tungsten Replicator

page 20
そこに... Riverプラグインを発見

page 21
elasticsearch-river-jdbc
概要
ElasticsearchのRiverプラグインとして動作する
JDBCドライバを指定することで各種RDBからレコードを流し込める
SQLを一定間隔で実行し、その結果を基に取り込みを行う
実テーブルだけでなくVIEWテーブルからも取り込める

page 22
elasticsearch-river-jdbc
不都合な点
動作が安定せずElasticsearchサーバを再起動する必要がある
しばらく動いていたがいつの間にか止まっている現象
Elasticsearchサーバ側の役割が増え、単機能ではなくなる
同期する度にElasticsearch側のドキュメントが空になる挙動

page 23
MySQL BinlogAPIが役立つのでは?

page 24
試してみたものの・・・

page 25
安定稼働への道が遠く、挫折...

page 26
そこで!

page 27
欲しいものが無いので作りました

page 28
4. Yamabiko

page 29
Yamabiko

https://github.com/y-ken/yamabiko
page 30
Yamabiko
概要
MySQLからElasticsearchへデータを非同期に逐次反映
Amazon RDS・MariaDB・PerconaServer等の互換DBにも対応
Elasticsearchとは別の単体ミドルウェアとして動作
CentOS 6.x向けのRPMパッケージとして配布中
任意のSQL文の結果の差分から、insert/update/deleteイベントを検知
SELECT * FROM contents WHERE DATE_ADD(updated_at, INTERVAL 5
MINUTE) > NOW(); といったクエリで差分同期も可能
page 31
Yamabiko
ユニークな特徴:delete検知が出来る
PrimaryKeyのギャップ判定を行うことで実現
行が物理削除されてしまうケースでも追従可能
数十万行単位でも動作します
なぜ更新ログ(BinaryLog)ではなくSQLの結果を同期するのか?
JOIN無しで検索するnoSQL的概念に対応させるため
非正規化VIEWテーブルを作ることを想定
page 32
Yamabikoシステム構成例
mysql_replicator_multi を利用する場合
Yamabikoが使うメタデータを

INSERT/SELECT

格納するためのMySQLを指定
同期情報管理テーブル
全文検索

更新/削除判定用のハッシュテーブル
同期する行数がさほど無ければ
データ参照元に相乗りしても良い

page 33
5. 検索ことはじめ

page 34
データ投入
かねてから無償配布されているライブドアグルメのCSVデータを利用
MySQLをGUI管理できるMacアプリ”Sequel Pro"を用いてDBへ流し込む
Yamabikoを利用し、MySQLからデータの同期を始める
登録クエリ:SELECT * FROM restaurants WHERE closed = 0;
次のトピックは後ほど解説
mapping:緯度経度を扱えるgeo_point型を使うため
analyzer:高機能な全文検索を扱えるKuromojiを使うため

page 35
page 36
検索クエリ
curl -X POST 'http://localhost:9200/livedoor-gourmet/_search?pretty' -d '{
"query":{
"query_string":{
"query":"name:ワイン AND pref_id:13"
Luceneクエリで条件指定
}
いわゆるWHERE句
},
"sort" : [
並び替え条件を指定
{ "access_count" : "desc" },
ORDER BY access_count DESC ...
"fan_count",
"_score"
],
範囲選択 LIMIT 0, 10
"from" : 0, "size" : 10
}'
page 37
意外と簡単!

page 38
緯度経度検索
MySQLでのGeo検索はイケてないのです
SPATIALインデックスを使えるのはMyISAMだけ
最近はInnoDBでGeohashを用いればある程度のことは出来る
Geohashにも対応しているElasticsearchであれば、Z曲線のグループ毎に
facet検索できるので、地域毎に何件ヒットしたかの検索も(多分)可能
MySQL側で緯度と経度が別カラムに格納されているテーブルを
Yamabikoを使ってElasticsearchに流し込む場合には、
SELECT文に CONCAT(lat, ",", lon) AS location という列を追加し、
次ページのmapping定義をすれば緯度経度や近傍検索が出来る
page 39
緯度経度検索
# mapping定義を行った後にデータを流し込む
$ curl -XPUT "http://localhost:9200/livedoor/restaurants/_mapping" -d '
{
"restaurants" : {
"properties" : {
"location": {
"type": "geo_point",
"lat_lon": "true"
}
}
}
}'
page 40
緯度経度検索
# 検索例 渋谷近辺で店名に「ワイン」を含むお店を10件リストアップします
$ curl -XGET "http://localhost:9200/livedoor/restaurants/_search?pretty" -d '
{
"query" : {
"filtered" : {
"query":{
"query_string":{
"query":"name:ワイン"
}
},
"filter" : {
"geo_distance" : {
"distance" : "1km",
"location" : {
"lat" : 35.3924,
"lon" : 139.4157
}
}
}
}
},
"from" : 0, "size" : 10
}'

page 41
意外と簡単!

page 42
Mapping
個別指定したい場合には全てのフィールドを定義しなくとも、
個別指定したいカラムのみのmappingをPOSTすることでも動く
indexやtypeの動的生成をする場合の注意
フィールドの型を推論してくれるのは便利だが、意図せぬ挙動を
防ぐためにも、実運用時はきちんと定義しておきたい
初回に投入されたドキュメントの内容でカラムが決まるため
MySQLと違い、後でMappingの定義の変更は不可
パフォーマンス的観点からも、not_analyzedも含めて指定すると良い
デフォルトではString型の文字は全てngramで分解するため
page 43
Analyzer
Kuromojiの利用方法
環境:Elasticsearch 1.0.0 RC2 RPM
注意点
次のような手順でプラグインをインストール
indexを作る前に登録する必要がある(既にある場合は作り直し)

プラグインを入れた後はelasticsearchを再起動する
$ cd /usr/share/elasticsearch
$ sudo ./bin/plugin --install 
elasticsearch/elasticsearch-analysis-kuromoji/2.0.0.RC1
$ sudo /etc/init.d/elasticsearch restart
page 44
Analyzer
$ curl -XPUT 'http://localhost:9200/livedoor/' -d'
{
"index":{
"analysis":{
"tokenizer" : {
"kuromoji" : {
"type" : "kuromoji_tokenizer",
"mode" : "extended"
}
},
"analyzer" : {
"analyzer" : {
"type" : "custom",
"tokenizer" : "kuromoji"
}
}
}
}
}'
page 45
6. Elasticsearch雑感

page 46
MySQL脳から見たElasticsearch

page 47
Elasticsearch ステキな所
MySQL同様にRPMパッケージを利用して手軽に構築できる
Solrと異なり、完全RestAPIで操作できる
Facet検索は大変便利
配列型はタグ検索にも大変便利
Luceneクエリは学習コストが低く直感的
複数カラムに渡る検索条件を指定しても速い
MySQLと異なり1テーブル1インデックスという制約が無いため
複数のソートキーを重み付けしながら使える (boost)
緯度経度周りの検索がMySQLのそれよりも高機能である
page 48
Elasticsearch ハマり所
Query DSLは奥が深く実に複雑なため、未だに自在に操れません...
mapping定義を更新するためには、index/typeごと消す必要があること
列はinsertAPIやbulkAPIでは自動追加されないため、updateAPIを用いる
noSQL的な概念の理解
GROUP BYに相当する機能は無い
GitHubの Field Collapsing/Combining · Issue #256 の今後に期待
MySQLでは正規化さえしておけばSQL言語で自在に扱えるが、JOINが
出来ないElasticsearchでは厳しい。そのため、取り出したい形式に合
わせた非正規化データ構造を入念に準備する必要があること
page 49
Elasticsearch ハマり所
noSQL故にJOIN出来ないということは、結果として各typeに同じデータ
を複製された状態で持たせることになり、一貫性が失われるという懸念
mappingやデータ登録時の_parentや_childを使うと類似の事は可能
ただし、1:Nとしての紐付け、つまりLEFT JOIN的な事は不可能
こちらのページの「Parent & child」が分かりやすいのでオススメ
Fun with elasticsearch's children and nested documents - Space Vatican
http://www.spacevatican.org/2012/6/3/fun-with-elasticsearch-schildren-and-nested-documents/

page 50
7. まとめ

page 51
まとめ

Elasticsearchの手軽さは革命的
ElasticsearchはKibanaの為だけのプロダクトではない
Yamabikoを使えば、並行運用しつつスモールスタートが可能

page 52
お知らせ
お知らせ
MySQLユーザ視点での小さく始めるElasticsearch
MySQLユーザ視点での小さく始めるElasticsearch
MySQLユーザ視点での小さく始めるElasticsearch
Air Intern にてオフィス動画を公開中
http://airintern.com/company/detail/livesense

LIVESENSE made* にてエンジニア求人掲載中
http://made.livesense.co.jp/
Thanks!
ご清聴ありがとうございました。

page 59

More Related Content

MySQLユーザ視点での小さく始めるElasticsearch