[英]Improving performance of Rails model
我有以下 model 允許用戶在照片上投票。
class Vote < ActiveRecord::Base
attr_accessible :value
belongs_to :photo
belongs_to :user
validates_associated :photo, :user
validates_uniqueness_of :user_id, :scope => :photo_id
validates_uniqueness_of :photo_id, :scope => :user_id
validates_inclusion_of :value, :in => [-2,-1,1,2], :allow_nil => true
after_save :write_photo_data
def self.score
dd = where( :value => -2 ).count
d = where( :value => -1 ).count
u = where( :value => 1 ).count
uu = where( :value => 2 ).count
self.compute_score(dd,d,u,uu)
end
def self.compute_score(dd, d, u, uu)
tot = [dd,d,u,uu].sum.to_f
score = [-5*dd, -2*d, 2*u, 5*uu].sum / [tot,4].sum*20.0
score.round(2)
end
private
def write_photo_data
self.photo.score = self.photo.votes.score
self.photo.save!
end
end
這個功能非常好,但是計算一張照片的分數非常慢——平均需要 7-12 秒。 我嘗試為photo_id
、 user_id
添加索引,以及為photo_id
和value
添加一個索引,但據我所知,這並沒有真正提高性能。
我會對任何認真的 Rails 專家(我完全是個業余愛好者)關於如何優化/改進的反饋感興趣。 您將如何計算對特定照片和價值的投票?
謝謝!
- 編輯 -
請注意,分數: -2,-1,1,2
表示“兩個拇指向下,一個拇指向下,一個拇指向上,兩個拇指向上”,而不是具體值。 我可以將這些與我在計算分數方法中分配給它們的值相匹配,但到目前為止我還沒有這樣做,因為我可能希望在看到更多數據積累后隨着時間的推移調整權重。
此外,無論我如何在數據庫中表示這四個可能的投票,我仍然需要每種投票的 COUNT 以及每張照片的這些投票的加權值來計算分數。 謝謝!
您可以做的一件事是向數據庫詢問一次而不是四次來獲取分數:
Vote.where(photo_id: photo.id).group(:value).count
將導致單個數據庫查詢並為您提供 hash 之類的
{-2 => 21, -1 => 48, 1 => 103, 2 => 84}
除此之外,如果您在數據庫中存儲[-5, -2, 2, 5]
而不是[-2, -1, 1, 2]
的實際值,您可以這樣做
Vote.where(photo_id: photo.id).sum
並直接從數據庫中獲取總和(或者甚至使用avg
來獲取平均值)
你需要一個價值索引,就其本身而言。 組合索引僅在查詢具有兩個組件時才起作用,從左側開始。 由於您的 where 子句沒有指定照片 ID,因此它沒有使用您的組合索引。
更新見http://dev.mysql.com/doc/refman/5.0/en/multiple-column-indexes.html
為什么存儲 -2、2、1、2 而不是實際成績? 如果您存儲成績(例如 -5),您將能夠直接在 DB 中計算分數,而無需運行 4 個計數查詢。 這肯定是一個進步。
如果數據庫中有大量記錄,在值列上放置索引將加快 SELECT 的速度。
上面的帖子也提出了一些直接優化的好處。 但是,隨着您的數據庫擴展,所有這些方法最終都會失敗。 由於分數是派生值,您可以將其緩存在 Memcached、Redis 甚至 SQL 中,這將確保獲取分數隨着應用程序的增長在恆定時間內擴展。 您可以允許緩存過時並使用后台進程保持更新。 通過這樣做,您的計算 function 可以花費任意時間而不會影響用戶體驗。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.