74
page May, 2014 23th MySQLと組み合わせて始める 全文検索プロダクト”elasticsearch” Kentaro Yoshida in 最新インフラエンジニア技術勉強会@ドリコム 1

MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

Embed Size (px)

DESCRIPTION

2014年5月開催の最新インフラエンジニア技術勉強会での発表スライドです。 本邦初公開のelasticsearch_mysql_importerの紹介をしました。 https://github.com/y-ken/elasticsearch_mysql_importer

Citation preview

Page 1: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

May, 2014

23th

MySQLと組み合わせて始める全文検索プロダクト”elasticsearch”Kentaro Yoshida in 最新インフラエンジニア技術勉強会@ドリコム

1

Page 2: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

1. 自己紹介2. はじめに3. 今回のテーマ4. Yamabikoの紹介5. 新作の紹介6. まとめ

本日の流れ

2

Page 3: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

1. 自己紹介

3

Page 4: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

自己紹介

4

•よしけんさん• (株)リブセンス•Web系インフラの研究開発エンジニア

• elasticsearch歴:2013年 初夏~

好きなプロダクト

Page 5: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

お知らせ

Page 6: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
Page 7: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

2. はじめに

7

Page 8: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

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

8

Page 9: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

MySQLを利用している

9

Page 10: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

だけれども、

10

Page 11: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

モダンな検索機能が欲しい

11

Page 12: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

インクリメンタルサーチ

12

Page 13: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

ファセット検索

13

Page 14: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

サジェスト機能

14

Page 15: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

位置情報検索

15

Page 16: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page 16

ネスト構造を用いた検索

案件に紐づく最寄り駅情報等に便利(elasticsearchにあってSolrには無い機能)

Page 17: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page 17

RestfulなAPI

Page 18: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

そして、

18

Page 19: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

検索漏れが少ない日本語全文検索

“Kuromoji”を使いたい!

19

Searchモード・Extendedモードが秀逸

Page 20: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

そんな時には

20

Page 21: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page 21

Page 22: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

“elasticsearch”がいまアツいです

22

Page 23: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

“elasticsearch” v1.0.02014年2月にリリース

23

Page 24: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page 24

Page 25: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

“elasticsearch”の時代がやってきた

25

Page 26: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

これは使いたい!

26

Page 27: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

しかし課題が残る

27

Page 28: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

“MySQL”とのデータ連携28

Page 29: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

3.今回のテーマ

29

Page 30: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

今回のテーマ

30

実データを用いて手軽にelasticsearchと連携した検索を行いたいelasticsearchをスモールスタートで使い始めたい既存プログラムの更新系処理に触れずに小さく始めたい

メインRDBはMySQLではあるが、検索のみelasticsearchを使う構成Amazon RDS for MySQLにも応用できる手離れの良い構成にしたい

MySQLサーバの管理無しに冗長化構成を実現できる (Multi-AZ)

Page 31: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

MySQLのレコードをelasticsearchへ同期したい

31

Page 32: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

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

32

Page 33: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

そこで!

33

Page 34: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

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

34

Page 35: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

4. Yamabiko

35

Page 36: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

Yamabiko

36

https://github.com/y-ken/yamabiko

���������� ����� � ����������������� ����� � ����

����������

��� �������� ��

���� ������������������ ��

�������������

���� ������������������ ��

��������

����������� �������������������

������������ !"#$���������%&'(��")*

� �� ����������+,-./0�1�2345%6789:

�������������

���

���

������������

Page 37: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

Yamabiko

37

概要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 38: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

Yamabiko

38

ユニークな特徴:delete検知が出来るPrimaryKeyのギャップ判定を行うことで実現行が物理削除されてしまうケースでも追従可能

数十万行単位でも動作します

なぜ更新ログ(BinaryLog)ではなくSQLの結果を同期するのか?JOIN無しで検索するnoSQL的概念に対応させるため非正規化VIEWテーブルを作ることを想定

Page 39: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

Yamabikoシステム構成例

39

mysql_replicator_multi を利用する場合Yamabikoが使うメタデータを格納するためのMySQLを指定同期情報管理テーブル更新/削除判定用のハッシュテーブル

同期する行数がさほど無ければデータ参照元に相乗りしても良い

INSERT/SELECT

全文検索

Page 40: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

しかし新たな課題が生まれる

40

Page 41: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

Yamabikoの差分検知速度が遅い問題

41

各行のハッシュ値の比較を行うため

Page 42: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

そこで作りました!

42

Page 43: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

5. 新作の紹介

43

Page 44: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

elasticsearch_mysql_importer

44

特徴MySQLからelasticsearchへデータを流し込む手間を最小化するツールYamabiko同様に、ドキュメントのネスト構造化が可能

Yamabikoで実現した差分検知を行い、差分更新/削除をするよりも、indexをその都度作り直し、都度完全同期する方が高速であったelasticsearchのBulk APIを利用するためのファイルを生成する機能基本的にそれだけのため、とてもシンプル

GitHub.com・ RubyGems.org にて「本日」公開!

Page 45: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

elasticsearch_mysql_importer

45

https://github.com/y-ken/elasticsearch_mysql_importer

Page 46: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

利用例

46

実装無しにelasticsearchにMySQLのレコードを流し込んで検索したい

1レコードに複数紐付く属性情報(最寄り駅・友達リスト)などを、ネスト構造で持たせて検索したい

非リアルタイム更新で差し支えないWebサービスでの利用

求人情報・賃貸物件情報・グルメ情報・商品情報・口コミ情報など

都度インデックスを作り直すため、小規模~中規模のWebサービスに最適

100MB / 100万件程度のデータボリュームを想定

Page 47: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

indexの設計例

47

全文検索情報を更新する度に、利用するindexを切り替える手法稼働中のindexには触れずに、都度新たにindexを生成する RDBに接続先のindex名を保存してアプリ側から動的に利用する例: index名-group_a, index名-group_b の2つをローテーション 例: index名-2014.05.23_210020(2014年5月23日 21:00:20)index毎にLuceneのshardが作られるため、影響の限定化が可能(更新中の不正終了等でデータが壊れるときはindex単位のため)

Page 48: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

想定システム構成

48

利用サーバをデプロイ毎に切り替える、blue-green deployment手法

Page 49: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

使い方

49

# レポジトリをクローンする$ git clone https://github.com/y-ken/elasticsearch_mysql_importer.git$ cd elasticsearch_mysql_importer$ bundle install --path vendor/bundle

# exampleファイルのMySQLの接続先やクエリを書き換える$ vim example.rb

# スクリプトを実行し、Bulk APIで登録するファイルを生成$ bundle exec ruby example/example.rb

# 生成された”requests.json”をelasticsearchへPOSTする$ curl -s -XPOST localhost:9200/_bulk --data-binary @example/requests.json

Page 50: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

使い方

49

# レポジトリをクローンする$ git clone https://github.com/y-ken/elasticsearch_mysql_importer.git$ cd elasticsearch_mysql_importer$ bundle install --path vendor/bundle

# exampleファイルのMySQLの接続先やクエリを書き換える$ vim example.rb

# スクリプトを実行し、Bulk APIで登録するファイルを生成$ bundle exec ruby example/example.rb

# 生成された”requests.json”をelasticsearchへPOSTする$ curl -s -XPOST localhost:9200/_bulk --data-binary @example/requests.json

Page 51: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

使い方

50

$ cat example.rbrequire 'elasticsearch_mysql_importer'

importer = ElasticsearchMysqlImporter::Importer.newimporter.configure do |config| config.mysql_host = 'localhost' config.mysql_username = 'your_mysql_username' config.mysql_password = 'your_mysql_password' config.mysql_database = 'some_database'

# ネスト構造にする際に設定(オプション) config.prepared_query = 'CREATE TEMPORARY TABLE ...snip...'

# 取り込むクエリを指定(必須) config.query = 'SELECT ...'

# elasticsearchのユニークキーに使うキーを指定(必須) config.primary_key = 'member_id'

# elasticsearchに登録するindexとtypeを指定(必須) config.elasticsearch_index = 'importer_example' config.elasticsearch_type = 'member_skill'

# ファイル出力先のパスを指定(必須) config.output_file = 'requests.json'end

importer.write_fileputs importer.output_file

Page 52: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

使い方

50

$ cat example.rbrequire 'elasticsearch_mysql_importer'

importer = ElasticsearchMysqlImporter::Importer.newimporter.configure do |config| config.mysql_host = 'localhost' config.mysql_username = 'your_mysql_username' config.mysql_password = 'your_mysql_password' config.mysql_database = 'some_database'

# ネスト構造にする際に設定(オプション) config.prepared_query = 'CREATE TEMPORARY TABLE ...snip...'

# 取り込むクエリを指定(必須) config.query = 'SELECT ...'

# elasticsearchのユニークキーに使うキーを指定(必須) config.primary_key = 'member_id'

# elasticsearchに登録するindexとtypeを指定(必須) config.elasticsearch_index = 'importer_example' config.elasticsearch_type = 'member_skill'

# ファイル出力先のパスを指定(必須) config.output_file = 'requests.json'end

importer.write_fileputs importer.output_file

Page 53: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

使い方

50

$ cat example.rbrequire 'elasticsearch_mysql_importer'

importer = ElasticsearchMysqlImporter::Importer.newimporter.configure do |config| config.mysql_host = 'localhost' config.mysql_username = 'your_mysql_username' config.mysql_password = 'your_mysql_password' config.mysql_database = 'some_database'

# ネスト構造にする際に設定(オプション) config.prepared_query = 'CREATE TEMPORARY TABLE ...snip...'

# 取り込むクエリを指定(必須) config.query = 'SELECT ...'

# elasticsearchのユニークキーに使うキーを指定(必須) config.primary_key = 'member_id'

# elasticsearchに登録するindexとtypeを指定(必須) config.elasticsearch_index = 'importer_example' config.elasticsearch_type = 'member_skill'

# ファイル出力先のパスを指定(必須) config.output_file = 'requests.json'end

importer.write_fileputs importer.output_file

Page 54: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

ネスト構造化の仕組み

51例としてこれらのテーブルを用いて解説します

Page 55: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

ネスト構造化の仕組み

52

$ curl -XGET http://localhost:9200/sample/member_skill/1?pretty{ "_index" : "sample", "_type" : "member_skill", "_id" : "1", "_version" : 1, "found" : true, "_source" : { "member_id" : 1, "member_name" : "ユーザA", "skills" : [ { "skill_name" : "PHP", "skill_url" : " /" }, { "skill_name" : "Ruby", "skill_url" : " /" } ] }}

Page 56: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

ネスト構造化の仕組み

52

$ curl -XGET

{ "_index" : "sample", "_type" : "member_skill", "_id" : "1", "_version" : 1, "found" : true, "_source" : { "member_id" : 1, "member_name" : "ユーザA", "skills" : [ { "skill_name" : "PHP", "skill_url" : "http://php.net/" }, { "skill_name" : "Ruby", "skill_url" : "https://www.ruby-lang.org/" } ] }}

Page 57: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

ネスト構造化の仕組み

53

-- prepared_query設定に記述する一時テーブル作成クエリCREATE TEMPORARY TABLE tmp_member_skill SELECT members.id AS member_id, skills.name AS skill_name, skills.url AS skill_url FROM members LEFT JOIN member_skill_relation ON members.id = member_id LEFT JOIN skills ON skills.id = skill_id;

Page 58: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

ネスト構造化の仕組み

53

-- prepared_query設定に記述する一時テーブル作成クエリCREATE TEMPORARY TABLE tmp_member_skill SELECT members.id AS member_id, skills.name AS skill_name, skills.url AS skill_url FROM members LEFT JOIN member_skill_relation ON members.id = member_id LEFT JOIN skills ON skills.id = skill_id;

Page 59: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

ネスト構造化の仕組み

54

-- query設定に記述する、elasticsearchへ登録するドキュメントを生成するクエリ。SELECT members.id AS member_id, members.name AS member_name, "SELECT skill_name, skill_url FROM tmp_member_skill WHERE member_id = ${member_id}" AS skillsFROM members ここを展開してネスト構造化します

Page 60: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

ネスト構造化の仕組み

55

-- クエリ実行結果にあるmember_idの値である1をプレースホルダに代入し、SQLクエリを実行します-- 実行結果を先ほどのskillsの値として代入しますSELECT skill_name, skill_url FROM tmp_member_skill WHERE member_id = 1

Page 61: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

ネスト構造化の仕組み

56

-- skillsの中を事前に作成したテンポラリテーブルから問い合わせた結果に置き換えてネスト構造化は完了です{ "member_id" : 1, "member_name" : "ユーザA", "skills" : [ { "skill_name" : "PHP", "skill_url" : "http://php.net/" }, { "skill_name" : "Ruby", "skill_url" : "https://www.ruby-lang.org/" } ]}

Page 62: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

ネスト構造化の仕組み

56

-- skillsの中を事前に作成したテンポラリテーブルから問い合わせた結果に置き換えてネスト構造化は完了です{ "member_id" : 1, "member_name" : "ユーザA", "skills" : [ { "skill_name" : "PHP", "skill_url" : "http://php.net/" }, { "skill_name" : "Ruby", "skill_url" : "https://www.ruby-lang.org/" } ]}

Page 63: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

とても便利!

57

Page 64: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

7. まとめ

58

Page 65: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

まとめ

59

elasticsearch が本格的に使えるプロダクトへと成長した

elasticsearch_mysql_importer を使えば手軽に始められる

elasticsearch の国内トレーニングや日本語書籍もあります

Page 66: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page 60

http://purchases.elasticsearch.com/class/elasticsearch/core-elasticsearch/tokyo/2014-05-20

2014年7月14日~16日に開催されます

Page 67: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page 61

http://ascii.asciimw.jp/books/books/detail/978-4-04-866202-4.shtml

Page 68: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

お知らせ

Page 69: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

お知らせ

Page 70: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
Page 71: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
Page 72: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
Page 73: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"
Page 74: MySQLと組み合わせて始める全文検索プロダクト"elasticsearch"

page

Thanks!

68

ご清聴ありがとうございました。