December 30, 2008
Apache Solrというのは、Javaベースの検索エンジンシステムです。
「ソーラ」と呼ぶそうです。どうしても覚えられません。
実はモバツイッターにも、秘かにツイッターのログ検索なる機能が追加してありまして、モバツイのエゴサーチなどをして、不具合がないかを調べていたりします。
検索エンジンはmysql + sennaを使っているのですが、自分のマシンのスペックよりも、データ量が増えてしまった状態らしく、ヒット数が多い「tinyurl」などの文字列で検索すると、めっさ遅いという状態になってしまいました。
おそらくmysqlの設定などはまだまだ余地があるんでしょう、と、いろいろ工夫しようとしたのですが、どうせならsenna以外も使えるようになりたいなぁと思って、こちらのtwitter検索で使われているSolrってのがあるというお話を聞いたので、Java久々だし使ってみようかなと思ったのが経緯。
どうでも良いけどSolrって、どうしても無意識にsolorってタイプしてしまいます。設定とかソースコード上で間違えるんですよね。。。
Solrというのは、検索エンジンライブラリのLucene(ルシーン)というJavaで作られた検索エンジン本体(ライブラリ)のフロントエンドとも言えるプロダクトで、ネイティブJava以外のアプリから繋ぐ場合は、http経由でインデックスの登録や検索機能を提供してくれたりします。
モバツイはPHPで動いてますが、PHPからアクセスするのに簡単に扱える「PHP Solr Client 」というPHPライブラリもありますので、さくっと利用することもできます。
参考:
PHP と Apache Solr によるエンタープライズ検索
このライブラリがやっていることは、ルールに従ったデータをXMLやクエリストリングで、file_get_contentsして戻ってきたXMLを連想配列に入れて返すというものですから自分でも作るのは難しくありません。
Solrそのものについて興味がある人は、ぐぐってもらえば先人の方々の書き込みがそれなりに存在するので、そちらをご参考ください。
で、このエントリーでは、そういう情報を見て、僕なりにここ数字やってみてハマってることなどをメモしておきます。
1.進化の激しいオープンソース系プロダクトの話題は日付とバージョンが欲しい!
僕が今使っているSolrのバージョンは1.3です。
Solrもそうだし、コアコンポーネントのLuceneは数年前から存在するので、時期が違うと古い記事との整合性で悩む時間が増えるんだよね。
これは多くの情報提供者に是非、意識して欲しいところだったりします。
多分、railsあたりも混沌としてるんではないかと。
2.Solr 1.3(Lucene)でsenを使った検索ってできますでしょうか?(自分で作れ、ってのではなく)
Solr、Luceneで日本語をうまく利用するためには、日本語を解析するフィルターっつうのかな。Analyzerと名付けられたプログラムを挟む必要があります。
調べると、MecabのJava実装であるsenを利用するか、solr組み込みのCJKAnalyzerといういかにも国際化を意識したライブラリのどちらかを利用すべしと出てきます。
Luceneで、senを使ったライブラリの話になると大体以下のリンクにたどり着きます。
が、ここに置いてあるLuceneのライブラリのバージョンが古い。
solr1.3に入っているLuceneは2.4devというバージョンですが上にあるのはLucene2.0のtest版まで。しかも2006年で進化が止まってる模様。
実際組み込んでみても、なにやらNoMethodErrorみたいなエラーが出て動きません。
これってバージョンの整合が取れてなくて新しいインターフェース的なものに対応してないとか、そんなところなのかなぁなんて思ってみましたが、それ以上は追っていません。
一応、動かなかったよ、ということを書き記しておきます。
CJKAnalyzerを使うように定義すれば日本語が使えるのでそっちでやってますが、sennaがMecabを使うので、なんとなくMecabの方が良いなぁなんて思ってたり。
3.Solrのフォルダ構造がよくわからない。
これは僕の都合の話です。antがわかってないからなのかなぁ。
なんでexampleフォルダという名前のフォルダの下にあるアプリでいろいろ進めてるんだろ。。。。
久々のJavaだったりもするので、なかなか難しいですね。
phpって適当に動かせばなんとかなるんだけど、Javaは作法があるのでそうもいかないんですよねぇ。
とはいえ、Jettyという軽量Webサーバで簡単に動かせるようになっています。
ダウンロードしたSolrのアーカイブファイルのexampleフォルダに移動して、
java -jar start.jar
と打つだけでJettyを使って動かすことができます。
RailsのWEBrickか、Apacheに対するLighttpdのような存在なのか、調べると結構普通に使えるというWebサーバらしいので、これでええんちゃう?と思って、モバツイにちょこっと組み込んでみました。
4.インデックス作成処理方法よくわかってないかも。
JettyをLinuxの起動スクリプトで動作させるようにして、モバツイのログ取得時に検索インデックス作るように通信してみたら、動作させた直後ぐらいから、
"503" Status: Error_opening_new_searcher_exceeded_limit_of_maxWarmingSearchers2_try_again_later
というエラーがでまくってたみたいで、今朝気がつきました。
この処理で、夜中のモバツイの動作がやや重くなってたみたいです。
僕が椅子落ちしている間にそんな状況になっていて、本日はコミケでモバイル環境が活躍する日にもかかわらず申し訳ございませんでした。
このエラーはSolrの同時接続数に限りがあるのでしょうか。
あまり検索エンジンの仕組みがわかってないから、そもそも並列でインデックス作るアクセスさせたらいかんということでしょうかね。
MySQL + Senna感覚で動かしてたのがマズイみたいで、今はバッチ処理で回すように切り替えています。(この記事はバッチ待ちに書いてます)
5.tomcatで動かす。
最初、tomcatで頑張ってたんですね。
tomcatは4とか5の変わり目以来使ってないので超久々ですが、なんとなく慣れてるつもりだったのですが、どうしても日本語の検索(GET)で文字化けしてしまう。
Connector周りで文字化け発生要因があることは僕も覚えていたので、
URIEncoding="UTF-8" useBodyEncodingForURI="true"
を設定しなさい的なところは沢山見つかる。
というか、ここまでの情報しか書いてないことが多いんだけど、実際検索の部分のGETパラメータに書いた日本語が文字化けするので、tomcatの都合で、これじゃ足りないこともすぐわかりました。
Jettyでは問題なく日本語検索できるので、Tomcatは一旦諦めたのですが、このブログを書くに当たって情報を調べていたら、以下のサイトに日本語処理についての話が載っていました。
hadoopとかsolrとかの実験 - Solrのインストール - myfinder -redMine-
こちらの方はサンプルアプリに、日本語エンコーディングをおこなうフィルターを追加していました。フィルターというのは、Servletが呼び出される前処理を可能にしているクラスです。
そこにTomcatのサンプルアプリについているフィルターをコピーして設置するという方法で対策されていたので、無事解決。
6.検索更新性能
また大量データを放り込んだ時の挙動は見れていませんが、仕事の方で一つsennaで課題に思っているのが、インデックスの更新時の検索パフォーマンスの低下というのがあります。
mysqlに数万件規模のinsert/updateを行い、sennaで大量のインデックス更新をさせてるときに、相応に重い検索が走ると、お互いとっても遅くなってしまうことがあります。それなりに沢山のアクセスがあると、クエリが山のように溜まってしまい、max connectionまで行ってしまったり。当然、Webサイトの方はよろしくない状態ですね。
重いSQLであること、インデックス更新中なのでクエリキャッシュも使えないため毎回新規の検索をしてるような状態らしく、この状態を回避するにはインデックス更新中は検索をさせないように複数台数のDBサーバのレプリケーションを切り替えながら回避しなきゃいけないかなと思ってたりします。
(何か良い方法があったら教えてほしいとこ。とりあえずメモリの量とデータ件数は関係ありそうだけど、無尽蔵にメモリ増やすわけにはいきませんしね。あと、仕事の方はmysql4なので、2indが使えないんですよね。。。)
Solrはどうなんだろう。バッチ時に処理する以上は、データ件数が多いtwitterでは多かれ少なかれ似たような状態になってしまうので、問題が健在化するかもしれず。
とりあえず有利そうに見える部分としては、コミット処理が別れてるので、なんか良いことあるのかなーとは。あと分散化できるかもってのも良いですね。
とりあえず現状、モバツイで試しているレベルだと、バッチでひたすらデータを放り込む処理はCPUは90%(Celeron 2.5GHz)を超えていますが、それに対しての検索はサクサクですね。
って別にsennaもマシンのメモリに対してデータがそれなりの時には何も問題ないので、実際どうなのかはよくわかりませんね。
twitterのログを拾っていくと莫大なデータソースを得られるので、こういうのを試すのには絶好の題材だと思う。
7.結論
ぐぐってもそれなりに動かせますが、やっぱりあんまり真面目にドキュメント(FrontPage - Solr Wiki)をちゃんと読まないでやるのも厳しいですね。
取説嫁ということで。
#この年末年始の休みも結局、モバツイのメンテナンスで終わってしまいそうな気がしてきた。
#どうでも良いけど検索エンジン系、recommend系って会社名とかプロダクトのネーミングがややこしすぎのような。ブームなんでしょうか。
#Luceneの商用サービスを提供されてる会社の社長さんで、Solrの開発にも関われている方のblogが、Operaで前のページに戻れないページがある。JUGEMなんだけど何故だろう。
技術評論社
売り上げランキング: 54615
日本語構文解析の説明がわかりやすい
この本には大変お世話になってます
全文検索の理解が深まりました。
著者さんへ...サンプルファイル、5章は改訂した方が良いのでは?
こんにちは。twitter検索つくってます@penguinanaです。
年末ですがメンテナンスご苦労様です。
参考になるかわかりませんが4と6についてコメントさせてください。
Solrの更新には把握している限りで2つ大きな問題があります。
・更新がそもそも重い
・更新直後の最初のsortが遅い?
twitterログなどのデータはsort必須だと思うのですが、
二つの問題を切り離して考えたほうが良いと考えてます。
以下はSolr1.3/jettyでのことなのですが、
twitter検索でも追加をバッチにして、更新の回数自体を減らそうと
しました。しかし発言がインデックスされないのはいやだなと思い、
直近1週間のみを保持するSolrを別に立ててまずはそちらを検索して、
結果が足りなければマスターにも問い合わせる仕組みにしてみました。
データの多いマスターの更新は1週間に一度でもよいということになります。
更新コミット直後の「最初の」sort付きクエリの応答時間は900万件で33秒、
1週間のみ(130万件)のSolrで3秒でした。
/*
実行環境:
900万件のdateでのソート:
jettyに割り当てたメモリ:800MB
インデックスファイル:2GB(OSキャッシュ:1GB程度)
130万件のdateでのソート:
jettyに割り当てたメモリ:800MB
インデックスファイル:0.38GB
*/
小さいテーブルを2分ごとのバッチ、大きいテーブルはもっと長いスパンで
更新を出すようにしています。
小さいテーブルの大きさを小さくしてしまうと大きいテーブルの更新が増えるのでトレードオフですが、総じて応答速度は上がったと思っています。
いやいや、そもそも更新処理がCPU食うし遅い件:
もっと汎用的で洗練された方法だとLinkedInが開発したZoieが参考になりそうです。
これはSolrと同じようにLuceneを利用したプロジェクトです。
インデックス更新がいつでも瞬間的に終わる仕組みです。
http://code.google.com/p/zoie/
私はまだ試していないのですが(!)概要としては、
これはLuceneのRAMモードのインデックス2つ。(RAM-A,RAM-B)
ディスク上のマスターインデックスがひとつ。
合計3つのインデックスで構成されます。
まず、追加リクエストはRAM-Aに蓄積されます。(←非常に高速)
一定量が蓄積されるとディスクに転記されます、(←遅いです。)
この処理中に追加リクエストがあった場合はRAM-Bに蓄積されます。(←非常に高速)
転記が終わるとRAM-AとRAM-Bを交換します。
交換したRAM-A(旧RAM-B)がいっぱいになるとまたディスクに転記、、
これを繰り返すため、インデックス更新は常に瞬間的に終わるかのように
クライアントからは見える仕組みです。
検索は、古いディスク上のインデックスと新しいデータが入ったRAMインデックスの両方から実行しマージするようです。
結局レプリケーションに類するアイデアですが比較的低コストで実現できるように思います。
(zoieは検索してみた限り誰も使っておらずもう少し様子見したほうがよさそうですが^^;)
Solr(Luceneの問題かも?)にはもうひとつ問題があって、
コミット直後の「最初のsort付きクエリ」が非常に遅いというのが
あります。上記の数値はsort付きクエリでのものですが、sortなしクエリだと
コミット直後でも結構速いのです。SolrのMLを見ているとどうやらsortに
使うフィールドのユニークキーが増えるほどこの時間は延びるようです。
最初のクエリだけ遅いというのはどういう仕組みなんでしょうね・・
必要なメモリも増えていくようですし弱点と言えるかもしれないです。
Luceneベースの更新が多いシステムではなんらかの対策が必要だという
ことのようです。私も更新がらみで少し悩んでおりましてレプリケーションしか
ないのかなーと考えていましてコメントさせていただきました。
いきなり長文失礼しました・・
参考になるかどうかわかりませんが、Solr1.3についてくるJettyにSenを組み込む手順について、Solrのzipの解凍からゼロベースでやってみました。
Solr1.3(Jetty)でSenを使えるようにする設定方法
http://blog.livedoor.jp/maru_tak/archives/50644974.html
>penguinanaさん
なるほどねぇ。。。
ソートが早くない、更新時のパフォーマンスはよろしくないってのは、如何ともし難い問題ってことなんですね。
1度目以降のソートが早いってのはメモリにキャッシュをしてるかしてないかってあたりなんでしょうかね。
つまりプロダクトの差はそんなにあるわけじゃなくて、どれだけオンメモリに載せられるかという基本的な話に戻るんですね。
mysql + sennaなんかでもupdateされるとクエリキャッシュなどはリセットされてしまうので、twitterのような、ひたすら更新しているものに使っているとひたすら遅い、という理屈ですよね。
またメモリ以外の対策としても分割処理という、これまた基本的な手を取るということですよね。
PFIさんのsedueのベンチマークが以下のURLに載っていますが、差別化要因についても、まさしくメモリ命という仕組みのあたりに集約されていく感じです。
http://preferred.jp/sedue/asbenchmark.php
インデックスはソートされた状態で保存されるようにしないといけないのかぁ。インデックス構築時にソートオーダーを指定できるような検索エンジンがあれば良いのになぁ。
>Takさん
ふむ。ということは、できてるってことですね。
なんで動かなかったんだろう。。。
SEN_HOMEを起動時に指定しないと動かないってあたりかもしれませんね。
ありがとうございます。試してみます!
Solrにデータを70万件ほど放り込んでみたら、commitとoptimizeがめっさ重かったし、その間は全く検索できず、サーバ一台で使うには無理があるので、とりあえず使うのやめました。
結局、分割してかなくてはいけないなら、MySQLの方がやりやすいので、とりあえずsennaに戻ってきて、検索可能範囲を限定することで対処してます。
なかなか難しいですね。
## zoieも使ってみたいですね。
はじめまして。
4に関して、
solrconfig.xmlで、maxWarmingSearchersの設定が可能なようです。
> Solr(Luceneの問題かも?)にはもうひとつ問題があって、
> コミット直後の「最初のsort付きクエリ」が非常に遅いというのが
> あります。上記の数値はsort付きクエリでのものですが、sortなしクエリだと
> コミット直後でも結構速いのです。
以前Luceneのソースを見たときの記憶なので間違っているかもしれませんが、
Luceneはとりあえず全件のindexをデフォルト(=ソートなし)でなめる、という動作をしたと思います。
その上で、全件のindexから必要なソートと件数に合わせたindexのListを用意し、そのListのそれぞれに対応した実データを取得するため、
sort付きクエリはsort無しクエリと比べて遅くなります。
最初でなければ早いのは、SolrかLuceneのキャッシュでしょうか。
> Solrにデータを70万件ほど放り込んでみたら、commitとoptimizeがめっさ重かったし
ハードウェアの性能に依ると思いますが、commitは数百万程度のオーダーであれば数秒もかからずレスポンスがあったように思います。
ただしその量のデータがある状態でOptimizeが複数同時に行われるとI/Oが無茶苦茶遅くなりました。
あとSolrはLucene単体に比べると非常にメモリを必要としますね。
>>結局、分割してかなくてはいけないなら、MySQLの方がやりやすいので、とりあえずsennaに戻ってきて、検索可能範囲を限定することで対処してます。(f-shinさん)
お疲れ様です。Sennaは開発者の方も更新系のシステムを念頭においておられるようですし(*1)、
なるほどTwitter関係ではよい選択となるということでしょうか。参考になりました。
*1:http://d.hatena.ne.jp/tasukuchan/20081222/sen_memory_exhausted
>>solrconfig.xmlで、maxWarmingSearchersの設定が可能なようです。(名無しさん)
私もキャッシュやウォーミングオプションを中心に設定しました。ほかにautowarmCountとかも0にしました。
効果はあったと信じていますがきちんと検証してないです。
キャッシュをOFFにするのは本末転倒ですが更新が多いシステムでは有効なんでしょうかね。
>>その上で、全件のindexから必要なソートと件数に合わせたindexのListを用意し、そのListのそれぞれに対応した実データを取得するため、 ・・・
実データとか取得しちゃうんですか。もし実際にそうなのであればSolrがメモリをやたらに使ってしまうのも頷ける気が・・・w