QA@IT

人気記事を表示するために、アクセスログをどうテーブルに保存するべきか

5020 PV

現在Rails3.2でWebサービスを作成しているのですが、テーブル設計、アルゴリズムで悩んでいるので質問させていただきました。

このサービスは一般的なブログサービスと考えてください。
つまり独立したコンテンツが複数個あり、ユーザがindexからタイトル等でコンテンツをクリックすると、コンテンツの詳細が表示されるものです。

このサービスに、ニコニコ動画の様に期間(昨日、一週間、月間)を指定して、その期間内に最も多く見られたコンテンツのTOP100を表示するという機能を追加したいと考えました。

しかし、そのためにはどのようなテーブルを追加したらいいのかわからず困っています。

私が考えたのは、
Historyというテーブルを作りこの列名に、

id,  content_id, count, created_at

と列を定義しました。

Historyはコンテンツ(以後 Contentクラス)と、多対1の関係を持ちます。content_idはその関係を結ぶ為の列です。

そして
コンテンツにアクセスされたら(ControllerのShowメソッドが呼ばれたら)、

history = content.histroys.where(:created_at => Date.today..Date.today+1) ##このコンテンツの子供の中で、今日から明日未満に作られたヒストリーを探す。書いてませんが該当しなかったら、新規に作成します。
history.count++  ##countをインクリメントする
history.save

という様なコードを実行することで、コンテンツの日毎のアクセスログをDBに保存するようにしました。

しかしうまくいえないのですが、どうもスマートな実装な感じがしません。
アクセスログのトップを表示するときにも複雑なSQL文を書かなくてはいけない様に感じます。

改めて質問なのですが、どのように実装するのがいいでしょうか?
ご意見いただけると幸いです。

回答

Railsのコード的にスマートかはわかりませんが、テーブルがすでにカウントにしか使えないというのはよくないように思います。カウントにしか使わないとすれば、historyという名前は微妙ですね。

想定しているPV数にもよりますが、アクセスログをためていくと件数が増えてきたときに容量・パフォーマンス(集計とインデックスメンテナンスコスト)などの問題が発生する場合がありますので、
集計タイミングやテーブルの分割は別途考えないといけないかもしれません。

容量的に厳しい場合はカウント方式も考えないといけないかもしれませんね。それでも当日分か数日分はなるべくそのまま残しておいた方がよいと思います。

なにをもって1件とするかが他のやり方でまかなえる且つ当日分の集計が不要であれば他の方が言うようにWebサーバーのアクセスログを別途集計してもいいかもしれません。

編集 履歴 (0)

Oakbow さんのおっしゃるようにアクセスログは別途保存したほうが良いと思います。
Webサーバの機能にあるかもしれません。

そのうえで、それを手早く利用するために、DBに仮集計を取っておいたら如何でしょう?
日付とコンテンツIDをキーに、アクセス数を集計したテーブルです。
日に1度だけ前日分を集計すれば、簡単なクエリでどの期間にでも対応できます。

表示したい期間が(昨日、一週間、月間などのように)決まっているなら、期間ごとにテーブルを作って、前述の処理後に本集計して日々更新すれば、更に速く表示できます。

編集 履歴 (0)

複雑なSQLを書くとスマートじゃないんですか?
なんか大きな勘違いをしている気がしますが。。。

さて、ログの記録ってことなので私だったらTreasure Dataとかを使えないか検討しますが、RDBMSを使うのであれば、ひたすらINSERTすると思います。
ログ書き込みでSELECTやUPDATEなんて無駄なことはしません。

日毎のアクセス件数を取るのは集計時にやればいいのであって、素材であるログは用途を限定せず、例えば時間ごとの集計が欲しくなっても対応できるようすべて書き込みます。

編集 履歴 (1)

ただのカウンターなら、KVSのようなNoSQLでもよいのでは?
また、HTTPサーバーのアクセスログを集計する方法もあると思います。

編集 履歴 (0)

皆様お返事ありがとうございます。

いただいた回答が多かったので、こちらでお返事とさせていただきます。

皆様のご回答を参考に、
Apachのログをrotatelogsで一日毎に保存し、CronでRubyの正規表現を回して、一日毎の各ブログのトータルアクセス数をDBに保存して、それに検索をかける方法でいこうと思います。

この方法にした理由は、

もし時間毎になっても、rotatelogsの値を変更して、テーブル設計をある程度変えれば、比較的簡単に変更が行える事。

アクセスされるたびにSQLが走らないので、サーバの負荷が低い事

です。

ご回答ありがとうございました。

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。