2007年04月05日
ブログサーチ顛末記2 サーバー構成

昨日に引き続き、作成したブログ検索サイトについて。本日は、サーバー構成についてまとめておきます。
サーバーの構成は以下のとおり。
・Webサーバー兼リバースプロキシーサーバー×1
・アプリケーションサーバー×1
・DBサーバー×2(マスター×1、スレーブ×1)
・クローラー×1
という5台構成になっています。
現状、アクセスの負荷がないため、サービスインができる本当の最小構成です。
もう少し詳しく見ていきます。
ハードウェア
CPU:Pentium D 820(2.80GHz)
メモリ:Webサーバー、アプリケーションサーバーには1GB、
DBのマスターに2GB、スレーブに3GB、クローラーに2GB
ディスク:SATA 80GBのソフトウェアRAID
ソフトウェア
Webサーバー兼リバースプロキシーサーバー:Apache 2.2+mod_proxy_balancer
アプリケーションサーバー:Apache 2.0、PHP5.1
DBマスター:MySQL 4.0.27
DBスレーブ:MySQL 4.0.27+senna
クローラー:MySQL 4.1.20+Plaggerベースのクローラー
※それぞれのバージョンの理由なんかは、また改めて述べます。
トラフィックが増えてきた場合に、アプリケーションサーバーとDBスレーブを増やしていけば対応できるような構成にしています。ここらへんは、はてなのシステム構成などを参考にしています。
まるごとPerl!の事例編や、Software Design 1月号の特集なんかが大変参考になります。
とまぁ、こんな感じのサーバー構成になっています。
次回はブログ検索サイトがどう動いているのかについてまとめてみます。
ブログのエントリーをどのようにして収集し、データベースに格納し、検索出来るようにしているのか、ということについてです。楽しみにしている人がいるかどうか分かりませんが、お楽しみに。
ついでのお話。
ブログ検索サイトとしてNAMAANを目標としたのはいいのですが、実際にNAMAANがどれくらいのデーターを収集しているのかが当初見当もつかず、またそういった情報を見つけ出すことも出来ませんでした。そのため、新たに作るとして、どれくらいのデーター量になるのか、サーバーを何台用意すればよいかが判断できずにおりました。
そこで、サーバーを用意する前に、まずは古いPCに開発環境を作成し、クローラーと簡単な検索インターフェイスを作成のうえ、ためしにブログのエントリーの収集を開始してみました。
そして、ある程度エントリーがたまったところで、いくつかのキーワードで返ってくる検索結果を元に、推測で必要なスペックを算出した次第です。
例えば10万件のエントリーを収集した時点で、“日本”というキーワードでヒットするエントリーが50件だとします。一方NAMAANでは、“日本”というキーワードでヒットするエントリーが5,000件あったとすると、その差は100倍。ならばNAMAANは1,000万件のエントリーを保存しているに違いない。などといった具合です(上記の数は適当な数値です)。
ひとつのキーワード自体では正確さに欠けますが、いくつものキーワードで探っていくと大体正解に近い数が導き出せるはずです。
上記で算出した値は、公開して良いものか分からないので伏せますが、今回作成したブログ検索サイトでは、毎日30万件程度の新着のブログエントリーを収集し、それを過去2週間分まで検索できるように、という仕様で作成しました。
投稿者 田中@グリニッジ : 00:24 | コメント (0) | トラックバック
2007年04月04日
ブログサーチ顛末記 そもそものきっかけ

昨年末より、ブログ検索のサイトを作ろうとしておりまして、技術的なことであれこれ格闘しておりました。行き詰っては調べて試行錯誤して、また調べて試行錯誤してを繰り返していたのですが、当初の想定していた性能を出せるくらいに作り上げることが出来ました。
あとはデザインを入れれば公開できるのですが、もともと技術面でのノウハウの習得がメインの目的だったこともあって、サービスを公開するという事自体はどうでも良くなっちゃった気もしていて、そんなことを考えているなか、知り合いから
「ブログ検索なんてたくさんあるじゃないですか」
なんて突っ込まれると、確かにいまさらブログ検索もなぁと思って、ちょっとモチベーションが低下中。
まぁ、いろいろなノウハウを習得できたこと、そして、それらを本業のほうにも生かせるであろう事から、良かったのではないかなと思うことにして。ちょうど本業のほうでの開発も調子が出てきたので、しばらくはそちらに注力することにして、このまま寝かせておいてまた考えます。
せっかくなので、ここ数ヶ月で学んだ事なんかを何回かに分けて書き残しておこうと思います。
そもそもは、blog検索エンジン「NAMAAN」の中身というブログエントリーをたまたま目にしたのがきっかけでした。
NAMAANでは、クローラーやインデックスサーバーなど、計12台のサーバーで構成されているとのこと。
もうちょっと少ないサーバー数で同等の性能を出そう、というのが目標です。
(論文が書かれた時点での話なので、現在のサーバー台数は知りません)
そんなわけで、とりあえず古いPCにCentOSをインストールし、基礎開発を行ってみたのが昨年末。その後、NAMAANの論文に書かれているサーバーと同等スペックのDELL SC430という格安サーバーを5台購入して本格的な試行錯誤がスタートしたわけでした。
サーバーが届いたのがちょうど今年の年明けぐらいだったと記憶しています。
ウノウラボにもこのDELLのサーバーを使っている写真が載っていたので、真似して、スチールラックにのっけてみたりなんかしちゃってます。
ちなみに現在、このDELLのサーバーは事務所に鎮座しております。非常に静かなサーバーで、騒音に悩まされることはありませんが、CPUがPentium Dプロセッサなので、結構発熱があり、また、UPSからもそこそこ発熱するので、結構ヒンシュクをかっています。
オフィスに置くには、Core2Duoサーバーがいいですね。
NAMAANとの性能比較は、同じキーワードを入れて検索をして、何件ヒットするか、最新エントリーの日時はどちらが新しいか、などを見比べて、この倍の性能を出さなきゃ、とかやっていたわけです。
NAMAANも「あなたのブログを最短1分で結果に反映」というのが売りのブログサーチですから、少ないサーバー数で対抗しようとするのは、なかなか手ごわかったです。
投稿者 田中@グリニッジ : 00:54 | コメント (0) | トラックバック
2007年01月29日
[Senna]インデックスの再生成

SennaのMySQLバインディングの話。
ふと気がつくと、Senna の正規化の機能(大文字、小文字、全角、半角を同一視してインデックスしてくれる機能)が働いていないようだったので、SHOW INDEX で調べてみたら、Index_type のところで NORMALIZE が欠落していました。alter table を行うと Senna のインデックス作成時指定のフラグ内容が失われてしまう、とはSennaのサイトに書いてありましたが、そのような処理は行ってなかったんですが、レプリケーションの設定で、マスターとの同期の際に書き換わってしまったのかもしれません。
ということでインデックスの再生成を行ったのですが、Googleとかで調べても、あまり情報が見つからなかったので記しておきます。
まずはインデックスの内容の確認
mysql> SHOW INDEX FROM table_name;
インデックスの削除
mysql> DROP INDEX index_name ON table_name;
インデックスの(再)設定
mysql> alter table table_name add FULLTEXT INDEX index_name USING SENNA, NORMALIZE (column_name);
最後にMySQLサーバーを停止し、MySQLのデータディレクトリに移動
# myisamchk -r -q table_name.MYI
インデックス作成時に、alter table ではなく、create index 構文でも良さそうだったんですが、私の環境ではうまく行きませんでした。その後の myisamchk の処理を行った後に、空のインデックスしか生成されず、しばし悩みました。Mecab が見つからないと空のインデックスが出来るらしいのですが、パスは通っているので、ちょっと???です。で、試しにと、alter tableでインデックスを設定すると、うまく行きました。
ブログサーチシステムは、少しずつ進行中。
やはり、クローラーのパフォーマンスをあげるのが難しいところ。
現在、1日30万件超の更新情報を処理し、非日本語のエントリーや重複エントリー、不正なエントリー(日付がおかしすぎる等)を省いて、毎日20万件くらいの新着登録を行えていますが、初期段階としてこの倍のパフォーマンスはほしいところ。
現在、クローラーは1台なので、台数をもう1台増やせば倍近くにはなりそうなのだけれども、プログラムの処理側でもう少し追い詰めてみたいところで、思案中。
DBはマスタ、スレーブ共に、RAID1にしているけれども、スレーブ側のインデックスの再構築がさほど時間かからずに完了できることが分かったので、スレーブ側はRAID0にして、冗長性よりも速度を取ろうかと思っています。
あとは、ユーザーインターフェイスの部分にそろそろ取り掛かっていこう。
多少はAJAXを使おうと思ってます。
投稿者 田中@グリニッジ : 17:06 | コメント (0) | トラックバック
2006年12月10日
[Senna]はまりどころ

Plaggerによるクローラーが集めてくるデーターがそろってきたので、検索部分を作ってみた。
で、はまったところ。
単語ひとつでの検索は問題がないのだが、複数語でのAND検索がどうもおかしい。
うまく検索が出来ていないようなので、検索のクエリーをいろいろ変えてみたりしたけれど解決せず。
Sennaのインストール用ドキュメントを改めて読んでみると以下のような記述が。
UTF-8やShift-JISでのインデックス作成を行う場合には、 > sudo mkdir /var/senna > sudo vi /var/senna/senna.conf DEFAULT_ENCODING utf8等、 文字コードを記述する(EUCの場合は不要。値はeuc, sjis, utf8のいずれか)
修正を行うと、AND検索もうまくできるようになった模様。
この部分の設定を行わなくても、普通に動作テストとかが通ってしまっていたゆえに、原因を見つけ出すのが少々困難で、はまってしまった。
投稿者 田中@グリニッジ : 21:59 | コメント (0) | トラックバック
2006年12月02日
[Plagger]クローラー部一応完成

前回のエントリーの波ダッシュの部分は、ちょっと考えを改めて、データーとしては変に変換せずに持つことにする。画面に出力する際に必要なら変換するように。
で、なかなかこのところ年末進行で時間が取れないんですが、大体クローラー部は出来上がりました。
工夫したところとしては、Subscription::PingServerLiberalで、あちらこちらのchanges.xmlをとってくると、UFT-8ではない文字が入り込んでいる場合があり、その場合に、Aggregatorがエラーを吐いて止まってしまうので、Plagger側で前処理を行うことも考えたけれど、考えを改め、PHPにパラメーターでchanges.xmlのURLを渡し、PHPにその内容の取得を変換を行たあと、Subscription::PingServerLiberalに渡すことにしました。
plugins:
- module: Subscription::PingServerLiberal
config:
fetch_items: 1
servers:
- url: http://....jp/parse.php?url=http://....com/changes.xml
PHPでの変換は主に以下のような感じ。
mb_convert_encoding($xmlStr, "UTF-8", "UTF-8");
mb_convert_encodingは、判断できないエンコーディングの文字は勝手に省いてくれます。
また、pingサーバーのchanges.xmlがときたまタイムアウトしたりして、その場合もAggregatorに影響を及ぼすので、その場合のハンドリングなんかもPHP側に任せています。
あとは、クローラーをまわし続けるために、最初は、cronで時間を決めて巡回させていたのですが、現在のテスト環境のマシンが超低スペックなので、処理に時間がかかると多重起動しまくりで、マシンがハングアップするので、ちゃんとプロセスを見て多重起動させないようにしました。
投稿者 田中@グリニッジ : 15:06 | コメント (0) | トラックバック
2006年11月26日
ブログサーチのクローラーのためのテストエントリ~

ようやくクローラーの精度が上がってきた。
結構いい加減なxmlとかが存在するので、いろいろと工夫が必要でひとすじ縄では行かないですね。
波ダッシュ(~)のマッピングがWindowsのUTF-8でおかしいので、
- module: Filter::Regexp
config:
regexp: s/\xE3\x80\x9C/\xEF\xBD\x9E/g
text_only: 1
を追記してみたけど、ちゃんと働くのだろうか。
うーん、うまく行かんね。
何か見落とししてるのかな。
投稿者 田中@グリニッジ : 22:20 | コメント (0) | トラックバック
2006年11月17日
[Plagger] とりあえず、DBに保存するところは出来た

PlaggerでDBに保存できるようにということで、Googleで調べていたら、Store::DBICというのを発見。
SVNのブランチの、feature_serverの中にありました。
これは、SQLiteしか対応してなさそうでしたが、よくみてみると簡単にMySQL用を作れそうだったので、ちょっと作業してみました。
assets/plugins/Store-DBIC/plagger.sqlite.sql
を元に、plagger.mysql.sqlを作成し、そのSQLにてテーブルを作成。また、
lib/Plagger/Schema
の中の
SQLite.pm
SQLite/Entry.pm
SQLite/EntryMeta.pm
SQLite/EntryTag.pm
SQLite/Feed.pm
SQLite/FeedMeta.pm
SQLite/FeedTag.pm
SQLite/Folder.pm
SQLite/Pin.pm
を元に、
MySQL.pm
MySQL/Entry.pm
MySQL/EntryMeta.pm
MySQL/EntryTag.pm
MySQL/Feed.pm
MySQL/FeedMeta.pm
MySQL/FeedTag.pm
MySQL/Folder.pm
MySQL/Pin.pm
を作成。
これは、SQLite.pmのものをそのまま使っても良さそうだったけど、とりあえず作ってみた。
あとは、config.yamlで
- module: Store::DBIC
config:
schema_class: Plagger::Schema::MySQL
connect_info: [ 'dbi:mysql:testdb', username, password ]
みたいな感じ。
config.yamlの中にパスワードを書くのがちょっと嫌かな。これは要検討
MySQL側はSennaバインディングでentryのbodyを全文検索できるようにした。
次は、Rule::Deduped::DBICかな。
こちらも存在しているのを見かけたので、簡単に行きそう。
それにしても、最近はClass::DBIではなく、DBIx::Classなんですね。
おじさん、時代遅れか。
WEB+DB PRESS Vol.35 になにやらnaoyaさんが書いているよう。
読み直してみよう。
投稿者 田中@グリニッジ : 20:18 | コメント (0) | トラックバック
2006年11月16日
SennaとHyper EstraierとPlaggerと、あとはいろいろマッシュアップで、ちょっと趣味プロジェクトのブログサーチを作るぞ計画。

Rule::Deduped の拡張 engineでMySQLを扱えるように
Plagger::Rule::Deduped::MySQL
Plagger::Rule::Deduped::DBI
みたいなのを作る
Rule::Dedupedは、compare_bodyオプションにてMD5 hashによるチェックを行う。
Rule::Fresh にて新着かどうかを判断
ブログの本文を取得するのにEFTやFilter::RetrieveEntry 以外の方法が無いのか検討中。
今のところ、EFTが一番なのかなぁ。
Filter::RetrieveEntry をベースに、ルールにマッチしない場合は置き換えないような形も検討。
ちょっと精度を上げる必要あり。というか、自分のBlogがうまく取得できないのを、テンプレートを変更することで逃げずに、何とかしたい。
feedに載ってこない過去のエントリや、過去のエントリが更新された場合にそれをどうキャッチアップできるは要検討。
Search関連のプラグインをチェック
そろそろベースの設計をまとめよう。
SennaバインディングのMySQLだから、MySQLクラスターは使えない。
複数ページにわたる検索結果の取得をする場合、ひとつのDBからデーターを取り出すならoffsetを簡単に処理できるが、複数の場合はそうはいかない。
DBごとのoffsetを検索クエリーに持たせれば大丈夫か。
とりあえず、初期開発版は、マスタを複数DBに分けることは考えずに開発することにしましょう。
でもクローラー用DBとストレージ用DBは分けておこう。
