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

Post on 02-Jul-2015

20.228 views 4 download

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

pixiv Solr導入記

X

自己紹介 松宮 孝大(mattun)

 プログラムエンジニア

◎pixivでかかわっているサービス  ・pixivモバイル  ・pixiv chat  ・pixivの検索全般

を担当していますJavaが好きなんですがpixivにはJava好きは居ません・・・

目次

Tritonn使用時の検索の問題点

Solrの検証

pixivでのSolrの最適化

まとめ

Solrを導入する目的

Tritonnによる検索に限界があるため検索専用のアプリケーションを模索

検索だけで30台ある台数を減らせればいいなぁ

モバイルで人気順ソートを実装する

Tritonn時代のマシン構成アプリケーション Mysql-Tritton(senna)を使用

マシン構成( 自作機 B28) x 30台 AthlonX2 4850e 2.50GHz (2コア) Mem 8G SSD X25(80GB) or C300(64GB)

でのTritonnの問題点

Mysqlの全文検索には更新時ロックがかかる MyISAMのためReplicationで更新クエリーがくるとそこでロックがかかってしまう

CPUのコア数でスケールできない ロックがかかるためCPUが1コア分くらいしか使い切れていない

R-18など数値のある文字が重い たとえば6を検索したとき⑥や全角半角の6などもOR検索し条件が増える 揺らぎ補正のためNormalizeはOffにできない

MySQLのバージョンをあげることができない Tritonnが組み込まれたバージョンを使用しなければならないため Mysql5.1などにアップグレードできなかった

何かの検索文字 R-18

東方 ( はいてない 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 下ろした OR 下ろして

OR 下ろそう OR 下ろさせ OR 下ろされ)

いろいろ重い検索条件を載せたかったのですがエロワードばかりなので自主規制・・・

これはやばい!

よし!Solrだ!

現在でも捌けないのに人気順ソートをモバイルに実装する必要がでてくる・・・

Solrの特徴

参照・更新ロックがかからない

レプリケーションが単純なファイル転送 Masterが持っているファイルをスレーブにコピーするだけ 内部でrsyncと同じようなことをしているだけ

スレーブの追加が簡単 設定ファイルを書くだけで起動すれば勝手に転送される Masterと同じインデックスファイルをもらうだけ

分散検索ができる

Normalizeや形態素解析やNGramなど目的に合わせたカスタマイズが可能

Commitが重い Solrはドキュメントを追加した後にCommitを行って初めてデータが反映される(トランザクションに近い?)  そのCommitが結構な負荷のため1件ずつリアルタイムで更新せず溜め込んだデータをバッチで一気に処理する

 pixivでは更新されたイラスト情報をMysqlのトリガーでログテーブルに保存しそのログを元にScalaで差分更新処理をしている

Commitのタイミングでキャッシュが消える 頻繁に更新するとクエリーキャッシュの意味を余り成さない

PHPやRubyにシリアライズされた結果を返してくれる 他にもXMLやJSONなどあり

Solr3.2.0を使用Solrの設定tokenizer : NGramTokenizer 指定された文字数で分割するfilter : LowerCaseFilter 大文字英字を小文字に変換charfilter : MappingCharFilterFactory 後ほど説明

データ構造uniqueKey: illust_idその他検索に必要なタグ、タイトル、コメント、作品がR18かどうかなどillust_id以外はインデックスのみでデータを持たない

Solr検証時の問題点

文字が長いと途中から検索できない・・・

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();

MultiValueを使うと検索結果がおかしい

unigramで2文字以上の検索ワードを投げるとタグがくっついた形ヒットする

例えばタグに[オリジナル] [パン]というタグがあったとして「ルパン」と検索すると・・・[オリジナル ] [パン] という様に分かれているタグが引っかかってしまう対処法が分からなかったため検索ワードの文字数によってunigramとbigramのインデックスを使い分けて対処した

オリジナル 猫の検索例  tag_bigram: オリジナル OR tag_unigram:猫

半角カタカナ文字にヒットしない半角カタカナを全角カタカナでヒットさせるには自前で設定をしなければならない(NGramTokenizerを使用する場合)

その場合MappingCharFilterFactoryというフィルターを使ってNormalizeすることができる設定例

"ガ"=>"ガ""ギ"=>"ギ"

pixivでは「へ」はどちらでもヒットするように設定"へ"=>"ヘ""べ"=>"ベ""ぺ"=>"ペ"

大量の件数がある場合ソートが重い全体で1200万件の内[東方]というタグには100万件以上ありソートだけで1.5秒くらいかかっている

1秒以上かかるものがある時点で検索を捌くことは不可能

データ更新は1分間隔でしていたのでキャッシュの恩威が少ない

ちなみにソートをしなければ一瞬で検索できる

じゃあ高速化しましょう!

高速化1SolrにはDistributedSearchという分散インデックス検索ができるのでインデックスを分散する

4分割(イラストID%4の余りが番号)して各マシンに1つずつインデックスを持てば検索速度が4倍早くなる

この機能は検索時にインデックスを持っているサーバーをパラメータで渡すだけでいいのでクライアント側の修正は簡単である

例q=東方&shareds=localhost:8983/solr/index0,localhost:8983/solr/index1

全体のインデックス

4分割された

イラストのイン

デックス

1つのイラストをイメージ

※補足 左に行くほど新しい

index1

index2

index3

index4

Solr1index1

Solr3index3

Solr2index2

Solr4index4

DistributedSearcher

すべてのインデックスから10件分(4分割なので40件分)の「東方」の結果を受け取る

クライアント

「東方」を検索する

その中で10件分の結果が返る

分散検索のイメージ図

4つのインデックスに投げる

高速化1の結果速度的には4分割するだけでかなりの高速化になった[東方]で1.5秒だったものが0.4秒くらいへ

それでも想定のマシン数では捌ききれない!

高速化2先ほどの4分割されたインデックスを古い日付のインデックスと見なし、新しい日付のイラスト(15万件)だけのインデックスを作り実質5分割にした

新しい日付イラストは1分間隔で差分更新する

古い日付のイラストは1時間隔で差分更新する

キャッシュの量を大量に設定

10万件以降のページングはキャッシュしない  <queryResultMaxDocsCached>100000</queryResultMaxDocsCached>  一度の検索で2万件のキャッシュを保持しておく <queryResultWindowSize>20000</queryResultWindowSize>

全体のインデックス

古い日付の

イラストのイン

デックス

最新の日付のイラスト1分更新

index1

index2

index3

index4

Index_new

高速化2の結果超高速化された!

古い日付のイラストにはSolrにあるクエリーキャッシュで返せる(キャッシュヒット率79%)

新しいイラストは件数が15万件なのでまったく重くない

問題点として古い日付のイラストのインデックスと最新の日付のインデックスに同じデータが被ってしまうと件数に不整合が起こるのでデータが被らないように調整する

全体のインデックス

最新の日付のイラスト

最新の日付のインデックスと古い日付のインデックスが被らないようにする

Index_new

古い日付の

イラストのイン

デックス

index1

index2

index3

index4

最終的にどれだけ高速化されたか?実質常にクエリーキャッシュが効いているため件数の多い[東方]でも1.5 秒 → 0.04~0.1秒などになった

Tritonnでは30台で捌けなかったものが6台で余裕となった

QPS的に言うとSolrのステータスでは200 / qpsと出ている

まとめ普通に使うだけでもTritonnより早い

分散サーチ機能がついているためスケールが簡単にできる。

過去のデータや不変なデータを別のインデックスに持つようにし更新頻度を抑えればキャッシュのヒット率が大幅にあがる

常に大量の更新がある場合はバッチ処理が必須

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