railsでコメント数をどう実装するかで悩んでいます。
ニコ動だとこんな感じ。
怖話でもこんな感じでコメント数/再生数などを保存しています。
一定以上の数になると素朴な処理では速度的に無理が来ます。
怖話での実装方法
怖話でのカウント数の実装の歴史。
- 何もせずDBに1レコードずつ保存時代。
- 一覧ページなどが重いのでrailsのcounter cahcheを使う。
- コメントや閲覧はpolymorphic関連なのでcounter cacheが対応してない。conditional_counter_cacheを使う。
- 削除に時間がかかり過ぎる。 ← イマココ
一覧の表示はconditional_counter_cacheで大丈夫なのですが、削除時の処理がかかりすぎてタイムアップする問題がでてきました。
削除時の問題
ここではわかりやすく閲覧数ではなくコメント数で説明します。
コメント数が1万件ある話を削除すると下記のような処理が走ります。
- storyを削除する。
dependent: :destroy
で依存するコメント1が自動的に削除される。- コメントが削除されたのでstoryのcomments_countが-1でUPDATEされる。
dependent: :destroy
で依存するコメント2が自動的に削除される。- コメントが削除されたのでstoryのcomments_countが-1でUPDATEされる。
- 以下1万回繰り返し
1件につき100msだとしても1000sかかるので無理がある。
これまでの考え
- memcacheやredisを使うのは手間なので無理が来るまで避けたい。(開発環境の構築の手間が増える)
- ランキングで集計するので時間の情報は欲しい。
- 一番楽な方法で実装しよう。
今の考え
- RDBは無理がある。memcacheやredis、他のストレージもやむなし。
- rails的に一般的な実装はなんだろう?
- ありがちな問題なので一般的な対処方法を構築したい。
皆さんこういうのどう実装されてますか?こんな風にやってるよという方がいらっしゃったら @komagata などにメッセージいただけるとありがたいです。 :bow:
再生orコメントされる頻度が高いほどカウンタ更新頻度が高くなるように(かつ永久にカウンタ更新されない動画が出来ないように)サービスを設計できるとよりベターですね
http://stackoverflow.com/questions/17803185/rails-counter-cache-while-destroy
こんな感じで、実際にdestroyが走る前に1万件分のレコードがすでに消えてればいいのではないでしょうか?
少なくともストレージの問題ではないと思います。問題がディスクからメモリに移行するだけで、1万回のクエリのRTTがボトルネックの場合には解決しないですね(3ms x 10,000 = 30s)。むしろRedisとかトランザクション外でやると失敗したときとか矛盾した状態が発生してもっと深刻な別の問題が出てくると思います。
・ そのケースだとcounter_cache使ってなくても `dependent: :destroy` なだけで遅そうなので `dependent: :delete_all` にする?
・ `story.destroy` の代わりに、`story.update(後でbatchで削除するフラグ: true)`