pixiv サイバーエージェント共同勉強会 solr導入記

22,399 views

Published on

0 Comments
38 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
22,399
On SlideShare
0
From Embeds
0
Number of Embeds
4,668
Actions
Shares
0
Downloads
85
Comments
0
Likes
38
Embeds 0
No embeds

No notes for slide

pixiv サイバーエージェント共同勉強会 solr導入記

  1. 1. pixiv Solr 導入記 X
  2. 2. <ul>自己紹介 </ul><ul><li> 松宮 孝大( mattun ) </li></ul><ul><li> プログラムエンジニア </li></ul><ul>◎ pixiv でかかわっているサービス   ・ pixiv モバイル   ・ pixiv chat   ・ pixiv の検索全般 <li>を担当しています
  3. 3. Java が好きなんですが pixiv には Java 好きは居ません・・・ </li></ul>
  4. 4. 目次 <ul><li>Tritonn 使用時の検索の問題点 </li></ul><ul><li>Solr の検証 </li></ul><ul><li>pixiv での Solr の最適化 </li></ul><ul><li>まとめ </li></ul>
  5. 5. Solrを導入する目的 Tritonn による検索に限界があるため検索専用のアプリケーションを模索 検索だけで30台ある台数を減らせればいいなぁ モバイルで人気順ソートを実装する
  6. 6. Tritonn時代のマシン構成 <ul><li>アプリケーション </li></ul>Mysql-Tritton(senna) を使用 <ul><li>マシン構成 ( 自作機 B28)   x 30台 </li></ul>AthlonX2 4850e 2.50GHz (2 コア ) Mem 8G SSD X25(80GB) or C300(64GB)
  7. 7. でのTritonnの問題点
  8. 8. <ul><li>Mysql の全文検索には更新時ロックがかかる </li></ul>MyISAM のため Replication で更新クエリーがくるとそこでロックがかかってしまう <ul><li>CPU のコア数でスケールできない </li></ul>ロックがかかるため CPU が1コア分くらいしか使い切れていない <ul><li>R-18 など数値のある文字が重い </li></ul>たとえば6を検索したとき⑥や全角半角の6なども OR 検索し条件が増える 揺らぎ補正のため Normalize は Off にできない <ul><li>MySQL のバージョンをあげることができない </li></ul>Tritonn が組み込まれたバージョンを使用しなければならないため Mysql5.1 などにアップグレードできなかった
  9. 9. <ul><li>何かの検索文字 R-18 </li></ul><ul><li>東方 ( はいてない OR 穿いてない OR はいてません OR 穿いてません OR ノーパン ) </li></ul><ul><li>( 髪 OR かみ )( ほどき OR ほどく OR ほどけ OR ほどい OR ほどいた OR ほどいて OR ほどこう OR ほどかせ OR ほどかれ OR 解き OR 解く OR 解け OR 解い OR 解いた 解いて OR 解こう OR 解かせ OR 解かせ OR 解かれ OR おろす OR おろし OR おろした OR おろして OR おろそう OR おろさせ OR おろされ OR 下ろす OR 下ろし OR 下ろした OR 下ろして OR 下ろそう OR 下ろさせ OR 下ろされ ) </li></ul>いろいろ重い検索条件を載せたかったのですがエロワードばかりなので自主規制・・・
  10. 10. これはやばい! よし!Solrだ! 現在でも捌けないのに人気順ソートをモバイルに実装する必要がでてくる・・・
  11. 11. Solrの特徴
  12. 12. <ul><li>参照・更新ロックがかからない </li></ul><ul><li>レプリケーションが単純なファイル転送 </li></ul>Master が持っているファイルをスレーブにコピーするだけ 内部で rsync と同じようなことをしているだけ <ul><li>スレーブの追加が簡単 </li></ul>設定ファイルを書くだけで起動すれば勝手に転送される Master と同じインデックスファイルをもらうだけ <ul><li>分散検索ができる </li></ul><ul><li>Normalize や形態素解析や NGram など目的に合わせたカスタマイズが可能 </li></ul>
  13. 13. <ul><li>Commit が重い </li></ul>  Solr はドキュメントを追加した後に Commit を行って初めてデータが反映される ( トランザクションに近い? )    その Commit が結構な負荷のため1件ずつリアルタイムで更新せず溜め込んだデータをバッチで一気に処理する   pixiv では更新されたイラスト情報を Mysql のトリガーでログテーブルに保存しそのログを元に Scala で差分更新処理をしている <ul><li>Commit のタイミングでキャッシュが消える </li></ul> 頻繁に更新するとクエリーキャッシュの意味を余り成さない <ul><li>PHP や Ruby にシリアライズされた結果を返してくれる </li></ul> 他にも XML や JSON などあり
  14. 14. Solr3.2.0を使用 <ul><li>Solr の設定
  15. 15. tokenizer : NGramTokenizer  指定された文字数で分割する
  16. 16. filter : LowerCaseFilter 大文字英字を小文字に変換
  17. 17. charfilter : MappingCharFilterFactory 後ほど説明
  18. 18. データ構造
  19. 19. uniqueKey: illust_id
  20. 20. その他検索に必要なタグ、タイトル、コメント、作品が R18 かどうかなど
  21. 21. illust_id 以外はインデックスのみでデータを持たない </li></ul>
  22. 22. Solr検証時の問題点
  23. 23. 文字が長いと途中から検索できない・・・ NGramTokenizer という Solr 標準の Tokenizer を使用してたのですが コードを読んでみると最大値がなぜか 1024 文字固定になっていたため可変に修正 solr-3.2.0/lucene/contrib/analyzers/common/src/java/org/apache/lucene/analysis/ngram/NGramTokenizer.java 修正前コード char[] chars = new char[1024]; input.read(chars);​ inStr = new String(chars).trim(); 修正後コード CharArrayWriter writer = new CharArrayWriter(1024); int c; while((c = input.read()) != -1) { writer.write(c); } inStr = new String(writer.toCharArray()).trim();
  24. 24. MultiValueを使うと検索結果がおかしい unigram で2文字以上の検索ワードを投げるとタグがくっついた形ヒットする 例えばタグに [ オリジナル ] [ パン ] というタグがあったとして 「ルパン」と検索すると・・・ [ オリジナ ル ] [ パン ] という様に分かれているタグが引っかかってしまう 対処法が分からなかったため検索ワードの文字数によって unigram と bigram のインデックスを使い分けて対処した オリジナル 猫の検索例    tag_bigram: オリジナル OR tag_unigram: 猫
  25. 25. 半角カタカナ文字にヒットしない 半角カタカナを全角カタカナでヒットさせるには自前で設定をしなければならない (NGramTokenizer を使用する場合 ) その場合 MappingCharFilterFactory というフィルターを使って Normalize することができる 設定例 &quot; ガ &quot;=>&quot; ガ &quot; &quot; ギ &quot;=>&quot; ギ &quot; pixiv では「へ」はどちらでもヒットするように設定 &quot; へ &quot;=>&quot; ヘ &quot; &quot; べ &quot;=>&quot; ベ &quot; &quot; ぺ &quot;=>&quot; ペ &quot;
  26. 26. 大量の件数がある場合ソートが重い 全体で1200万件の内 [ 東方 ] というタグには100万件以上ありソートだけで1 .5 秒くらいかかっている 1秒以上かかるものがある時点で検索を捌くことは不可能 データ更新は1分間隔でしていたのでキャッシュの恩威が少ない ちなみにソートをしなければ一瞬で検索できる
  27. 27. じゃあ高速化しましょう!
  28. 28. 高速化1 Solr には DistributedSearch という分散インデックス検索ができるのでインデックスを分散する 4分割 ( イラスト ID%4 の余りが番号 ) して各マシンに1つずつインデックスを持てば検索速度が4倍早くなる この機能は検索時にインデックスを持っているサーバーをパラメータで渡すだけでいいのでクライアント側の修正は簡単である 例 q= 東方 &shareds=localhost:8983/solr/index0,localhost:8983/solr/index1
  29. 29. ※ 補足 左に行くほど新しい 全体のインデックス 4分割された イラストのインデックス 1つのイラストをイメージ index1 index2 index3 index4
  30. 30. すべてのインデックスから10件分 ( 4分割なので40件分 ) の「東方」の結果を受け取る クライアント 「東方」を検索する その中で10件分の結果が返る 分散検索のイメージ図 4つのインデックスに投げる Solr1 index1 Solr3 index3 Solr2 index2 Solr4 index4 DistributedSearcher
  31. 31. 高速化1の結果 <ul><li>速度的には4分割するだけでかなりの高速化になった </li></ul>[ 東方 ] で 1.5 秒だったものが 0.4 秒くらいへ <ul><li>それでも想定のマシン数では捌ききれない! </li></ul>
  32. 32. 高速化2 <ul><li>先ほどの4分割されたインデックスを古い日付のインデックスと見なし、新しい日付のイラスト(15万件)だけのインデックスを作り実質5分割にした </li></ul><ul><li>新しい日付イラストは1分間隔で差分更新する </li></ul><ul><li>古い日付のイラストは1時間隔で差分更新する </li></ul><ul><li>キャッシュの量を大量に設定 </li></ul>10万件以降のページングはキャッシュしない    <queryResultMaxDocsCached>100000</queryResultMaxDocsCached>   一度の検索で2万件のキャッシュを保持しておく <queryResultWindowSize>20000</queryResultWindowSize>
  33. 33. 全体のインデックス 古い日付の イラストのインデックス 最新の日付のイラスト 1分更新 index1 index2 index3 index4 Index_new
  34. 34. 高速化2の結果 超高速化された! 古い日付のイラストには Solr にあるクエリーキャッシュで返せる (キャッシュヒット率 79 %) 新しいイラストは件数が15万件なのでまったく重くない <ul><li>問題点として古い日付のイラストのインデックスと最新の日付のインデックスに同じデータが被ってしまうと件数に不整合が起こるのでデータが被らないように調整する </li></ul>
  35. 35. 最新の日付のインデックスと古い日付のインデックスが被らないようにする 全体のインデックス 最新の日付のイラスト Index_new 古い日付の イラストのインデックス index1 index2 index3 index4
  36. 36. 最終的にどれだけ高速化されたか? <ul><li>実質常にクエリーキャッシュが効いているため件数の多い [ 東方 ] でも 1.5 秒 -> 0.04 ~ 0.1 秒などになった </li></ul><ul><li>Tritonn では30台で捌けなかったものが6台で余裕となった </li></ul><ul><li>QPS 的に言うと Solr のステータスでは 200 / qps と出ている </li></ul>
  37. 37. まとめ <ul><li>普通に使うだけでも Tritonn より早い </li></ul><ul><li>分散サーチ機能がついているためスケールが簡単にできる。 </li></ul><ul><li>過去のデータや不変なデータを別のインデックスに持つようにし更新頻度を抑えればキャッシュのヒット率が大幅にあがる </li></ul><ul><li>常に大量の更新がある場合はバッチ処理が必須 </li></ul>
  38. 38. ご清聴ありがとうございました

×