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

3,796 views

Published on

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

Published in: Technology
0 Comments
14 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,796
On SlideShare
0
From Embeds
0
Number of Embeds
189
Actions
Shares
0
Downloads
18
Comments
0
Likes
14
Embeds 0
No embeds

No notes for slide

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

  1. 1. page May, 2014 23th MySQLと組み合わせて始める 全文検索プロダクト elasticsearch Kentaro Yoshida in 最新インフラエンジニア技術勉強会@ドリコム 1
  2. 2. page 1. 自己紹介 2. はじめに 3. 今回のテーマ 4. Yamabikoの紹介 5. 新作の紹介 6. まとめ 本日の流れ 2
  3. 3. page 1. 自己紹介 3
  4. 4. page 自己紹介 4 • よしけんさん • (株)リブセンス • Web系インフラの 研究開発エンジニア • elasticsearch歴: 2013年 初夏∼ 好きなプロダクト
  5. 5. お知らせ
  6. 6. page 2. はじめに 7
  7. 7. page こんなお悩みを抱えていませんか? 8
  8. 8. page MySQLを利用している 9
  9. 9. page だけれども、 10
  10. 10. page モダンな検索機能が欲しい 11
  11. 11. page インクリメンタルサーチ 12
  12. 12. page ファセット検索 13
  13. 13. page サジェスト機能 14
  14. 14. page 位置情報検索 15
  15. 15. page 16 ネスト構造を用いた検索 案件に紐づく最寄り駅情報等に便利(elasticsearchにあってSolrには無い機能)
  16. 16. page 17 RestfulなAPI
  17. 17. page そして、 18
  18. 18. page 検索漏れが少ない日本語全文検索 “Kuromoji”を使いたい! 19 Searchモード・Extendedモードが秀逸
  19. 19. page そんな時には 20
  20. 20. page 21
  21. 21. page “elasticsearch”がいまアツいです 22
  22. 22. page “elasticsearch” v1.0.0 2014年2月にリリース 23
  23. 23. page 24
  24. 24. page “elasticsearch”の時代がやってきた 25
  25. 25. page これは使いたい! 26
  26. 26. page しかし課題が残る 27
  27. 27. page “MySQL”とのデータ連携 28
  28. 28. page 3.今回のテーマ 29
  29. 29. page 今回のテーマ 30 実データを用いて手軽にelasticsearchと連携した検索を行いたい elasticsearchをスモールスタートで使い始めたい 既存プログラムの更新系処理に触れずに小さく始めたい メインRDBはMySQLではあるが、検索のみelasticsearchを使う構成 Amazon RDS for MySQLにも応用できる手離れの良い構成にしたい MySQLサーバの管理無しに冗長化構成を実現できる (Multi-AZ)
  30. 30. page MySQLのレコードを elasticsearchへ同期したい 31
  31. 31. page つまり 異種RDB間のデータ同期 32
  32. 32. page そこで! 33
  33. 33. page 欲しいものが無いので作りました 34
  34. 34. page 4. Yamabiko 35
  35. 35. page Yamabiko 36 https://github.com/y-ken/yamabiko
  36. 36. 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(); といったクエリで差分同期も可能
  37. 37. page Yamabiko 38 ユニークな特徴:delete検知が出来る PrimaryKeyのギャップ判定を行うことで実現 行が物理削除されてしまうケースでも追従可能 数十万行単位でも動作します なぜ更新ログ(BinaryLog)ではなくSQLの結果を同期するのか? JOIN無しで検索するnoSQL的概念に対応させるため 非正規化VIEWテーブルを作ることを想定
  38. 38. page Yamabikoシステム構成例 39 mysql_replicator_multi を利用する場合 Yamabikoが使うメタデータを 格納するためのMySQLを指定 同期情報管理テーブル 更新/削除判定用のハッシュテーブル 同期する行数がさほど無ければ データ参照元に相乗りしても良い INSERT/SELECT 全文検索
  39. 39. page しかし新たな課題が生まれる 40
  40. 40. page Yamabikoの差分検知速度が遅い問題 41 各行のハッシュ値の比較を行うため
  41. 41. page そこで作りました! 42
  42. 42. page 5. 新作の紹介 43
  43. 43. page elasticsearch_mysql_importer 44 特徴 MySQLからelasticsearchへデータを流し込む手間を最小化するツール Yamabiko同様に、ドキュメントのネスト構造化が可能 Yamabikoで実現した差分検知を行い、差分更新/削除をするよりも、 indexをその都度作り直し、都度完全同期する方が高速であった elasticsearchのBulk APIを利用するためのファイルを生成する機能 基本的にそれだけのため、とてもシンプル GitHub.com・ RubyGems.org にて「本日」公開!
  44. 44. page elasticsearch_mysql_importer 45 https://github.com/y-ken/elasticsearch_mysql_importer
  45. 45. page 利用例 46 実装無しにelasticsearchにMySQLのレコードを流し込んで検索したい 1レコードに複数紐付く属性情報(最寄り駅・友達リスト)などを、 ネスト構造で持たせて検索したい 非リアルタイム更新で差し支えないWebサービスでの利用 求人情報・賃貸物件情報・グルメ情報・商品情報・口コミ情報など 都度インデックスを作り直すため、小規模∼中規模のWebサービスに最適 100MB / 100万件程度のデータボリュームを想定
  46. 46. 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単位のため)
  47. 47. page 想定システム構成 48 利用サーバをデプロイ毎に切り替える、blue-green deployment手法
  48. 48. 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する
  49. 49. 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
  50. 50. page 使い方 50 $ cat example.rb require 'elasticsearch_mysql_importer' importer = ElasticsearchMysqlImporter::Importer.new importer.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
  51. 51. page 使い方 50 # ネスト構造にする際に設定(オプション) 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'
  52. 52. page 使い方 50 # 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_file puts importer.output_file
  53. 53. page ネスト構造化の仕組み 51 例としてこれらのテーブルを用いて解説します
  54. 54. 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" : [ {
  55. 55. page ネスト構造化の仕組み 52 "member_name" : "ユーザA", "skills" : [ { "skill_name" : "PHP", "skill_url" : "http://php.net/" }, { "skill_name" : "Ruby", "skill_url" : "https://www.ruby-lang.org/" } ] } }
  56. 56. 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;
  57. 57. 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;
  58. 58. 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 skills FROM members ここを展開してネスト構造化します
  59. 59. page ネスト構造化の仕組み 55 -- クエリ実行結果にあるmember_idの値である1をプレースホルダに 代入し、SQLクエリを実行します -- 実行結果を先ほどのskillsの値として代入します SELECT skill_name, skill_url FROM tmp_member_skill WHERE member_id = 1
  60. 60. page ネスト構造化の仕組み 56 -- skillsの中を事前に作成したテンポラリテーブルから問い合わせ た結果に置き換えてネスト構造化は完了です { "member_id" : 1, "member_name" : "ユーザA", "skills" : [ { "skill_name" : "PHP", "skill_url" : "http://php.net/" }, { "skill_name" : "Ruby",
  61. 61. page ネスト構造化の仕組み 56 { "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/" } ] }
  62. 62. page とても便利! 57
  63. 63. page 7. まとめ 58
  64. 64. page まとめ 59 elasticsearch が本格的に使えるプロダクトへと成長した elasticsearch_mysql_importer を使えば手軽に始められる elasticsearch の国内トレーニングや日本語書籍もあります
  65. 65. page 60 http://purchases.elasticsearch.com/class/elasticsearch/core-elasticsearch/tokyo/2014-05-20 2014年7月14日∼16日に開催されます
  66. 66. page 61 http://ascii.asciimw.jp/books/books/detail/978-4-04-866202-4.shtml
  67. 67. お知らせ
  68. 68. お知らせ
  69. 69. page Thanks! 68 ご清聴ありがとうございました。

×