SunspotではじめるSolr入門

8,621 views
8,584 views

Published on

社内勉強会でSunspotとSolrについて開催したときの資料です。
Sunspotを初めて本番運用にのせる人向けに、Solrの基本的な機能や設定について紹介しました。
Railsは3.2と4.0を想定しています。

http://techracho.bpsinc.jp/baba/2013_08_17/12787

Published in: Technology

SunspotではじめるSolr入門

  1. 1. Sunspotではじめる Solr入門 2013/08/15 baba@bps 1
  2. 2. はじめに • 本資料は社内勉強会で使った資料です • 一部ページや解説を追加しています • Rails 4.0をベースに書いていますが、Rails 3.1 / 3.2でも動作確認 をしています • 再配布等のご相談はbaba@bpsinc.jpまでお願いします 2
  3. 3. はじめに • Railsは分かるけどSolrは全然分からない人向けです • 対象 • Railsの検索を高速化したい!Sunspotが良いと聞いたけど何これ? • Sunspotは試したことあるけどSolr分からないから設定を知りたい • Sunspot使ったサービスを公開したいけど、サーバ設定が分からん • 対象外 • Rails何それおいしいの • データは30件しかありません • 5億件のデータを分散処理する方法を探している • 全文検索エンジンの比較データが欲しい 3
  4. 4. これは何? • Sunspot • SolrをRailsで使いやすくするgem • Solr • Luceneに管理画面やキャッシュなどのフロントエンドを追加したもの • Lucene • Apache Projectで開発されている全文検索エンジン → 大量データの検索を速くするやつ 4 これは違います
  5. 5. 「Solrで始めるSunspot入門」 じゃないの? • Railsの人にとっては、理屈よりさっさとSunspot動かす方が簡単 • その後にSolrを直接触ってみよう 5
  6. 6. Sunspotを使ってみる 6
  7. 7. Gemfile # これが本体 gem ‘sunspot_rails’ # 自分でSolr入れるのめんどくさい # これを入れておくとrakeコマンドでSolrを起動できる gem ‘sunspot_solr’ 7
  8. 8. app/models/user.rb class User < ActiveRecord::Base searchable do text :email text :fullname do “#{first_name} #{last_name}” end boolean :is_admin integer :age # 他にもdateとかlatlonとか # multiple: trueで配列も使える end end 8
  9. 9. app/controllers/users_controller.rb def search @search = User.search do with(:is_admin, false) with(:age).greater_than_or_equal_to 20 fulltext “太郎” end end 9
  10. 10. app/views/users/search.html.erb <% @search.results.each do |user| %> <%= user.email %> <br> <% end %> 10
  11. 11. Run bundle rails g sunspot_rails install # => config/sunspot.ymlが生成されるだけ rake sunspot:solr:start rake sunspot:solr:reindex 11
  12. 12. 速い? 12
  13. 13. どのくらい速い? 0 2000 4000 6000 8000 10000 12000 100件 1万件 100万件 1億件 MySQL Solr 13 100件なら意味ない 1万件だと意味ある 100万件だと無いと死ぬ
  14. 14. なんで速い? MySQL Where LIKE • 先頭から順番に探す • O(n) Solr • 転置インデックスで探す • 索引 • O(log n) 14
  15. 15. Sunspotの動作 15
  16. 16. Sunspotの動作 save ① INSERT INTO users (email, age) VALUES (‘test@example.com’, 30); rails sunspot ② POST /solr/mycore/update ここでインデックス作る Commitまですると結構重い 16
  17. 17. Sunspotの動作 search ② SELECT * FROM user WHERE id IN (1,3); rails sunspot ① GET /solr/mycore/select 超速い IDがとれる 17 実際に使うデータはDBから ※Storedを使うと、①のみで終わらせることもできる
  18. 18. About Solr 18
  19. 19. Solr • Web API • Web管理画面 • Javaサーブレットコンテナで動く • dist/solr-4.4.0.war • Tomcat, Jetty, … 19
  20. 20. Solrのバージョン • 1.3 • マルチコア • 分散検索 • 1.4 • Javaベースレプリケーション • 3.1 • Luceneと統合されたのでバージョン飛んだ • 4.0 • 管理画面が格好良く • インデクシング高速化 • 4.4 • 現在の最新版 MANGA REBORN 1.3 この前やった某案件 4.3 20
  21. 21. 3つの起動方法 • sunspot_solr • お手軽 • version 1.3 (sunspot_solr 1.3) • version 3.5 (sunspot_solr 2.0) • パッケージインストール • 管理しやすい • root必要 • version 1.4 • apache.orgからダウンロード • 自由度高い • version 4.4.0 21 schema.xmlをSunspot用に書き換える必要あり
  22. 22. sunspot_solr bundle install rake sunspot:solr:start => default port: 8981(test), 8982(development), 8983(production) => config/data directory: ${RAILS_ROOT}/solr/conf ${RAILS_ROOT}/solr/data 実体はstart.jarを実行しているだけ jettyが起動する 22
  23. 23. Ubuntu Package sudo apt-get install solr-tomcat sudo service tomcat6 start => default port: 8080 => config/data directory: /etc/solr/conf /var/lib/solr/data 23
  24. 24. Download Archive wget ftp://ftp.riken.jp/net/apache/lucene/solr/4.4.0/solr-4.4.0.tgz tar xzf solr-4.4.0.tgz cd solr-4.4.0/example java –jar start.jar => default port: 8983 => config/data directory: solr-4.4.0/example/solr/conf solr-4.4.0/example/solr/data jettyが起動する 24
  25. 25. Download Archive # 別の設定を使う java –Dsolr.solr.home=multicore –jar start.jar # 自分でちゃんとセットアップする solr-4.4.0/dist/solr-4.4.0.war 25
  26. 26. 管理画面 26
  27. 27. 27
  28. 28. 28
  29. 29. 29
  30. 30. 30
  31. 31. 31
  32. 32. Ping ステータスチェック(nagiosに便 利) Query 検索を試せる Analysis 保存されたデータの統計 Schema Browser 動的フィールドを含め確認 32
  33. 33. 検索してみる 33
  34. 34. 検索してみる 34
  35. 35. 検索してみる • すべてのAdminユーザを取得 • http://localhost:8983/solr/mytest/ select?q=*%3A*&fq=type%3AUser &fq=is_admin_b%3A1&wt=json&i ndent=true • q • *:* • fq • type:User • is_admin_b:1 35
  36. 36. 検索してみる • 20歳未満 • q=*:* • fq=type:User • fq=age_i:{* TO 20} • 名前検索「太郎」 • q=太郎 • fq=type:User • df=fullname_text 36 Solr上ではすべてのモデルのデータがフラットに格納されている Sunspotが”type”フィールドを追加して、検索時に絞り込んでいる
  37. 37. Solrの設定 37
  38. 38. フォルダ構成 • solr • solr.xml • myproject1 • core.properties • conf • schema.xml • solrconfig.xml • … • data • … • myproject2 • core.properties • conf • schema.xml • solrconfig.xml • … • data • … 38
  39. 39. 重要な設定ファイル • solr.xml • 全体設定 • あまり変更しない • 4.3まではここにcore一覧を定義した • schema.xml • データ型 • TokenizerやFilterもここで設定 • Sunspot用のdynamicFieldを正しく設定しないと動かないので注意 • solrconfig.xml • ログ設定 • autoCommit設定 39
  40. 40. MultiCore 40 これじゃないです → 1つのサーバで複数プロジェクトのデータを扱えるようにする機 能。 • RDBMSでいう「データベース」 • コアごとに設定を変えられる
  41. 41. MultiCore • 初期のSolr • プロジェクトA • production:8983, development: 8982, test: 8981 • プロジェクトB • production: 8984, development: 8985, test: 8986 • 管理めんどくさい、リソースもったいない • マルチコア • 1つのポートでOK • localhost:8983/solr/projecta-production • localhost:8983/solr/projecta-development • localhost:8983/solr/projecta-test • わかりやすい、省メモリ • 1個に負荷かけると他にも影響する • 本番運用は注意 41
  42. 42. マルチコアのときのsunspot.yml development: solr: hostname: localhost port: 8983 path: /solr/myproject-development production: solr: hostname: localhost port: 8983 path: /solr/myproject-production 42
  43. 43. coreを増やす • solr • solr.xml • mycore • core.properties • conf • schema.xml • solrconfig.xml • …. • collection1 • core.properties • conf • … cd example/solr mkdir mycore cat “name=mycore” > mycore/core.properties cp –r collection1/conf mycore/conf # dataディレクトリはコピーしない! solr 4.4未満では core.propertiesではなくsolr.xmlの<cores>に定義する 増えていなければ、管理画面のCoreAdmin→Addで登録してやる 43 フォルダコピーで増やせる
  44. 44. solrconfig.xml • http://wiki.apache.org/solr/SolrConfigXml • キャッシュ設定 • ログ設定 • autoCommit設定 <autoCommit> <maxDocs>10000</maxDocs> <maxTime>1000</maxTime> </autoCommit> 明示的にcommitしなくても、10000更新または1000msごとに自動commit 44
  45. 45. schema.xml <schema> <types> <fieldType name="text" class="solr.TextField" omitNorms="false"> <analyzer> <tokenizer class="solr.WhitespaceTokenizerFactory"/> <filter class="solr.StandardFilterFactory"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.NGramFilterFactory" minGramSize="1" maxGramSize="1"/> </analyzer> </fieldType> <fieldType name="boolean" class="solr.BoolField" omitNorms="true"/> <fieldType name="date" class="solr.DateField" omitNorms="true"/> <fieldType name="sint" class="solr.SortableIntField" omitNorms="true"/> <fieldType name="sfloat" class="solr.SortableFloatField" omitNorms="true"/> </types> 45
  46. 46. schema.xml (つづき) <fields> <field name="id" stored="true" type="string" multiValued="false" indexed="true"/> <field name="type" stored="false" type="string" multiValued="true" indexed="true"/> <field name="class_name" stored="false" type="string" multiValued="false" indexed="true"/> <field name="text" stored="false" type="string" multiValued="true" indexed="true" /> <field name="lat" stored="true" type="tdouble" multiValued="false" indexed="true" /> <field name="lng" stored="true" type="tdouble" multiValued="false" indexed="true" /> <dynamicField name="*_text" stored="false" type="text" multiValued="true" indexed="true"/> <dynamicField name="*_texts" stored="true" type="text" multiValued="true" indexed="true"/> <dynamicField name="*_b" stored="false" type="boolean" multiValued="false" indexed="true"/> <dynamicField name="*_bm" stored="false" type="boolean" multiValued="true" indexed="true"/> <dynamicField name="*_bs" stored="true" type="boolean" multiValued="false" indexed="true"/> <dynamicField name="*_bms" stored="true" type="boolean" multiValued="true" indexed="true"/> <dynamicField name="*_i" stored="false" type="sint" multiValued="false" indexed="true"/> </fields> 46
  47. 47. schema.xml (つづき) <uniqueKey>id</uniqueKey> <defaultSearchField>text</defaultSearchField> <solrQueryParser defaultOperator="AND"/> </schema> 47
  48. 48. schema.xml • tokenizerを設定できる • KeywordTokenizer • 文字列を分割しない • LetterTokenizer • 非文字で分割 • WhitespaceTokenizer • 空白文字で分割 • StandardTokenizer • UAX#29に従って分割 • UAX29URLEmailTokenizer • URLやemailを認識 • JapaneseTokenizer • 日本語を分かち書き 48
  49. 49. schema.xml • filterを設定できる • LowerCaseFilter • 大文字を小文字に • TrimFilter • 前後の空白を除去 • StopFilter • ストップワードを除去 • EdgeNGramTokenFilter • 指定文字数のN-Gramを生成 • JapaneseBaseFormFilter • 「走れ」を「走る」のように基本形に変換 • JapaneseReadingFormFilter • 「日本」を「ニホン」のように読みに変換 49
  50. 50. dynamicField • フィールドを事前に定義しなくても、dynamicFieldの定義に一致 すれば自動的に作ってくれる • Sunspotが大量に定義 • 自前SolrでSunspotを運用するときは、dynamicFieldの整合性が保たれて いることが必須 • デフォルトのexampleにあるものとは衝突するので注意 • *_dはdate?double? • これがないと、検索対象フィールド増やすたびにschema.xmlの 変更が必要になる 50
  51. 51. Sunspot Note 51
  52. 52. index系メソッド • @user.solr_index • このユーザのsolr indexを更新 • after_saveで自動で呼ばれるが、関連テーブルは手動で呼んでやる必要がある • User.solr_index • 全ユーザのsolr indexを更新 • 非常に重い(データ量によっては1日で終わらない) • 検索対象(searchable)フィールドを増やしたときなど • User.solr_reindex • 全ユーザのsolr indexをいったん全削除してからindex • 検索対象(searchable)フィールドの意味を変更して互換性が失われたときなど • 処理中、検索がほとんどヒットしなくなるので注意 • rake sunspot:solr:reindex • 全モデルに対してsolr_reindex → インデックスを全部作り直す • solr_indexとindex • 単なるalias • indexとindex! • index!は、即座にSolr Commitを行う 52
  53. 53. 動かない、壊れた • ERROR: unknown field “type” • schema.xmlが間違っている可能性大 • sunspot_solrのものを使う • dataディレクトリを丸ごと消してみる • 当然インデックスは全部消えます • Connection refused • Solrは起動しているか • config/sunspot.ymlのport設定は正しいか • 404 Not Found • config/sunspot.ymlのpath設定は正しいか • No field configured for User with ‘name’ • searchのコードが間違っている • 一度もindexされていない • 全然ヒットしない • reindexしてみる • 管理画面で検索してみる 53
  54. 54. 更新中の検索 • dynamicFieldは、indexする際に自動的にスキーマが追加される • 一度もindexする前に検索すると • No field configuredなエラー • index処理中に検索すると • 最初の1件ですでにスキーマは生成されているので、エラーにならない • index済みのものしか検索にヒットしない • 検索するたびにヒット数が増えていく • 検索対象(searchable)フィールドを増やすとき • 順番が大事 • searchableを追加 → 最低1件indexしてスキーマ更新 → search部分デプロイ • 逆にすると検索がエラーになる • 検索対象(searchable)フィールドを減らすとき • 余分なスキーマがあっても(データ量以外)問題ない • 検索対象(searchable)フィールドの型を変更するとき • 型を変えるとSunspotが別名をつけてSolrでは別フィールドとして扱われる 54
  55. 55. まとめ 55
  56. 56. まとめ • Sunspotを使うとすぐにSolr検索を始められる • サーバで公開するなら最低限チェックしよう • どのSolrを使うか(sunspot_solr, パッケージ, マニュアルダウンロード) • アップデートや将来的なスケールにも影響する • バージョンが古いと一部機能が制限される • 設定ファイルはどれを使うか • 適当に選ぶとSunspotに不適切だったり検索結果で日本語がヒットしにくかったり • dataディレクトリはかなり膨らむので、ディスクスペースとIO負荷に注意 • ログはデフォルトだと非常に肥大化する • ポートは何番で、coreのパスはどうするか • この辺決めないと死活監視もアップデートもできない • 管理画面を使えるようになろう • デバッグには必須です • もっと色々調べてみよう • スケール、可用性、Tokenizer/Filter、パフォーマンスチューニング • とっても奥が深い 56

×