unoh.github.com

Tips for HyperEstraier

2008-10-10 08:45:36 +0000

先月、VX Revolution VX-Rを購入して、その使い心地の良さに素直に感動しているbokkoです。

HyperEstraier



HyperEstraierは平林幹雄さんが開発されている全文検索エンジンです。全文検索エンジンとして使えるのはもちろん、全文検索のためのライブラリとして使うこともできます。ウノウではHyperEstraier(以下HE)をフォト蔵の写真検索に利用しています。

今回はHEの活用や運用に関するTipsについて紹介します。

インデックスの作成



HEのインデックスを作成する方法はいくつかありますが、単にデータが空のインデックスを作るのであれば以下で十分です。

$ estcmd create idx


実際には想定されるインデックスのサイズなどに応じてオプションを追加するといったことが必要になるでしょう。ファイルやディレクトリ(内のファイル)をインデックスに追加するにはgatherコマンドを使います。

$ estcmd gather idx data # dataはファイルもしくはディレクトリ


gatherはデフォルトではサポート外の拡張子のファイルは無視するので、サポートされていない拡張子のファイルをインデックスに追加する場合は、-feオプションを付けましょう。

インデックスの最適化



HEのインデックスに追加されたデータは削除することが可能ですが、データがインデックスから削除されても削除された領域分、データが小さくなるわけではありません。つまり、インデックスからデータが削除されても、削除された領域はゴミとして残ります。これはインデックス内のデータへのアクセス速度に後々影響を与えるので、optimizeコマンドを使って、削除された領域を整理し、インデックスのサイズを縮小するとよいでしょう。

$ estcmd optimize idx


なお、インデックスの最適化はインデックスのサイズが大きいと時間がかかる上にCPUリソースをかなり消費するので、注意しましょう。また、インデックスの無駄な整理するのはpurgeコマンドに-clオプションを付けて実行することでも可能です。

$ estcmd purge -cl idx


インデックスの分割とマージ



HEのインデックスは件数が増えてくると段々(特に更新の)パフォーマンスが劣化し始めます。そのため、導入時などに件数の多いデータのインデックスを作成する際は、まず小さいインデックスをたくさん作り、後で各インデックスをマージしてまとめる方が効率がよいです。

$ estcmd merge idx1 idx2 # idx2をidx1にマージ


属性検索と属性インデックス



属性検索は非常に遅いので、使う場合はできるだけ属性インデックスを作成しましょう。ただ、それでも通常の検索に比べると遅い上にCPUリソースを結構喰うので、どうしても使いたい時だけ使いましょう。特にesmasterに対して検索リクエストを送って属性検索を行うとインデックスのサイズが大きい場合、時間がかかるだけでなく、最悪の場合、estmasterが落ちてしまう可能性があります(estmasterについては後述)。また、属性にインデックスを貼るとインデックスの更新が遅くなります。

$ estcmd create -attr @str_data str idx # 属性str_dataに文字列インデックスを貼る


estmasterの運用



HEのP2P機能やノードAPIを使ってアプリケーションを作成する場合、サーバプログラムであるestmasterを使用する必要があります。estmasterは検索やインデックスの更新、削除などのリクエストをHTTP経由で受け取り、実行します。estmasterを使うにはまず、専用のサーバルートディレクトリを以下のコマンドで作成します。

 $ estmaster init casket


間違っても既にあるサーバルートディレクトリを再度初期化しないようにしましょう。_node以下のインデックスが丸ごと消えます。(導入作業が佳境に差し掛かった頃に一度消して泣きそうになりました)estmasterを起動するには以下のコマンドを実行します。デフォルトではポート番号1978が使用されますが、この設定はサーバルート以下の_confファイルを編集することで変更できます。

 $ estmaster start casket


バックグラウンドで走らせるには-bgオプションを指定します。ただ、estmasterから検索を行う場合、estmasterが落ちてしまうと何もできなくなってしまうので、対策が必要になります。前回のdaemontoolsの解説でも述べましたが、フォト蔵ではestmasterの管理にdaemontoolsを活用しています。起動スクリプトは単純ですが、estmasterを扱う場合、例えば↓のような記述が必要になります。

SERVER_ROOT=/var/casket
PID_FILE=${SERVER_ROOT}/_pid
if [ -e $PID_FILE ]
then
    rm $PID_FILE
else
    :
fi


estmasterは起動すると、サーバールート以下に_pidというプロセスIDが書かれたファイルが作成され、終了するとこのファイルは削除されます。しかし、異常終了した場合はこのファイルが残ってしまい、estmasterを再起動しようとすると、estmaster自身は既に起動していると思い込んでしまい、起動することができなくなります。このため、runスクリプトで_pidの存在の有無を確認し、存在する場合は削除するようにします。

リクエストが受信できているか確認する



estmasterが正常に検索やそのほかのリクエストを受信できているか確認するには、サーバールート以下にある_logファイルの内容を確認します。実際には以下のようにtailコマンドを使うのがよいでしょう。

 $ tail -f _log


管理画面からの操作



HEにはestmasterやestmasterが使用しているインデックスを管理するためのWebインタフェースが用意されています。このestmasterの管理画面ではインデックスの作成やリンクの設定などいろんな操作ができますが、オプションを指定することができないので、実際のインデックスはestcmdコマンドで作成した方がいいでしょう。また、ノード間リンクの設定など、管理画面上から設定してもestmasterが終了すると設定が失われるものがあります。実際の設定変更を行う際は管理画面からではなく、estcallコマンドやAPI経由で行うのがよいでしょう。ただし、コマンドラインからestcallを使ってestmasterに命令を発行する際、ユーザ名とパスワードを直打ちしなければならないので、履歴にパスワードが残ってしまう可能性があります。なので、究極的にはAPIを使う方が良いでしょう。

検索用と更新用にインデックスを分ける



HEではインデックスを更新している間、そのインデックスに対して検索をかけることができません。このため、フォト蔵では検索用のインデックスと更新用のインデックスを明確に分けています。estmasterにHTTP経由で検索クエリを投げる処理はフォト蔵自体がPHPで書かれていることもあって、PHPで書いていますが、インデックスの更新スクリプトはPerlでHTTPを介さず、コアAPIを経由する形で書いています。

1文字検索



HEはN.M-gram方式(N=2、M=2)と転置インデックスにより、データが保存されているので、1文字で検索した場合、検索漏れが生じる可能性があります。1文字の場合はワイルドカード検索になり、一定回数だけ、データをインデックスから検索します。

アルファベットや数字から成る文字列の検索



HEはN.M-gram方式ではありますが、アルファベットや数字から成る文字列に対しては、N文字単位(N=2、後に続くM文字(M=2) )で転置インデックスを作成するようなことはせず、まとめて転置インデックスを作成します。このため、アルファベットや数字から成る文字列に対しては、検索キーワードが完全に一致しなければ検索に引っかからないという現象が発生します。例えば、インデックス内に「photozou」という文字列が格納されている文書がある場合、「photozou」で検索すれば、引っかかりますが、「photo」では引っかかりません。これはSennaやMySQLの全文検索インデックスでも似たような動作をします。理由としては全文検索において、上記のような文字列を含む文書に対して完全なN-gram(N=2)で転置インデックスを作成しても、インデックスが肥大化する割にはさほど効果がないことが挙げられます。HEでこの問題に対処するには、検索条件式を「通常書式」か「簡便書式」にして、キーワードの前方一致や後方一致を有効にします。検索条件式はoptionパラメータによって制御されているので、このパラメータに適切な値をセットすることによって、検索条件式を変更することができます。各検索条件式の値はestraier.hを見ると以下のように定義されています。

enum {                                   /* enumeration for options */
  ESTCONDSURE = 1 << 0,                  /* check every N-gram key */
  ESTCONDUSUAL = 1 << 1,                 /* check N-gram keys skipping by one */
  ESTCONDFAST = 1 << 2,                  /* check N-gram keys skipping by two */
  ESTCONDAGITO = 1 << 3,                 /* check N-gram keys skipping by three */
  ESTCONDNOIDF = 1 << 4,                 /* without TF-IDF tuning */
  ESTCONDSIMPLE = 1 << 10,               /* with the simplified phrase */
  ESTCONDROUGH = 1 << 11,                /* with the rough phrase */
  ESTCONDUNION = 1 << 15,                /* with the union phrase */
  ESTCONDISECT = 1 << 16,                /* with the intersection phrase */
  ESTCONDSCFB = 1 << 30                  /* feed back scores (for debug) */
};


通常書式の場合、前方一致を有効にするには検索キーワードを「[BW] photo」、後方一致を有効にするには検索キーワードを「[EW] photo」という具合に指定します。簡便書式の場合、前方一致を有効にするには検索キーワードを「photo*」、後方一致を有効にするには検索キーワードを「*photo」という具合に指定します。

ただし、1文字検索と同様、これはワイルドカード検索となり、一定回数だけしかデータをインデックスから検索しないので、検索漏れが生じる可能性があり、CPUの負荷も増大します。

estmasterが管理しているインデックスのバックアップ



estmasterは/master?action=backupがリクエストされると、サーバルート以下の_confファイル内のbackupcmdで指定されたバックアップコマンドを実行します。このリクエストを受け取るとestmasterはインデックスをディスクと同期させるので、比較的安全にバックアップを行うことができます。estmasterの起動中に、estmasterが管理しているインデックスをディスクと同期させずにコピーしたり移動したりすると、インデックスが壊れることがあるので、estmasterを起動させたまま、バックアップを取るときは必ずbackupcmdで指定したスクリプトで行いましょう。

パラメータの調整



サーバディレクトリ以下にある_confファイルを編集することによって、estmasterの動作を調整することができます。要はApacheのhttpd.confのようなものです。以下にいくつかのパラメータについて解説します。ほかのパラメータについては公式サイトを参照してください。

maxconn



ApacheでいうMaxClientsです。

searchtimeout



検索のtimeout値です。通常の検索に比べて遅い属性検索を使用する場合は少し高めに設定するのがよいでしょう。

backupcmd



/master?action=backupがリクエストされた際に起動するバックアップコマンドを指定します。

whildmax



先述の通り、HyperEstraierはN.M-gram方式(N=2,M=2)なため、1文字では検索漏れが生じる可能性があります。1文字の場合はワイルドカード検索になり、一定回数しかデータを検索しません。この回数を指定するのがwhildmaxパラメータであり、デフォルト値は256となっています。

参考文献・URL



全文検索システム Hyper Estraier
N.M-gram: ハッシュ値付きN-gram 法による転置インデックスの実現


また、余談ですが、検索に関する基礎的な知識やアルゴリズムについて理解したいのであれば、以下の書籍が参考になると思います。

情報検索アルゴリズム
北 研二 津田 和彦 獅々堀 正幹
共立出版
売り上げランキング: 145553
おすすめ度の平均: 3.5
3 基本知識の足しになれば
4 わかりやすくてよい