提高Rails模型的性能

时间:2011-05-24 23:00:55

标签: sql ruby-on-rails ruby-on-rails-3 optimization

我有以下模型,允许用户在照片上投票。

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_iduser_id添加索引,并为photo_idvalue添加一个索引,但就我所知,这并没有真正改善性能

我对任何严肃的铁路专家(我完全是业余爱好者)的反馈感兴趣,以了解如何优化/改进。你如何计算特定照片和价值的投票?

谢谢!

- 编辑 -

请注意,得分:-2,-1,1,2代表“双拇指向下,单拇指向下,拇指向上,双拇指向上”,而非特定值。我可以将这些与我在计算分数方法中分配给它们的值相匹配,但到目前为止我还没有这样做,因为我可能希望在看到更多数据累积后随时调整权重。

此外,无论我如何在数据库中代表这四种可能的投票,我仍然需要每种投票的COUNT以及每张照片的加权值来计算得分。谢谢!

4 个答案:

答案 0 :(得分:1)

您可以做的一件事就是要求数据库一次而不是四次获得分数:

Vote.where(photo_id: photo.id).group(:value).count

会导致单个数据库查询并为您提供类似

的哈希值
{-2 => 21, -1 => 48, 1 => 103, 2 => 84}

除此之外,如果您在数据库中存储[-5, -2, 2, 5]而不是[-2, -1, 1, 2]的实际值,您可以这样做

Vote.where(photo_id: photo.id).sum

直接从数据库中获取总和(或者甚至使用avg来获得平均值)

答案 1 :(得分:1)

您需要一个值索引。组合索引仅在查询具有两个组件时起作用,从左侧开始。由于您的where子句未指定照片ID,因此它不使用您的组合索引。

更新请参阅http://dev.mysql.com/doc/refman/5.0/en/multiple-column-indexes.html

答案 2 :(得分:0)

为什么要存储-2,2,1,2而不是实际等级?如果存储成绩(例如-5),您将能够直接在DB中计算得分而无需运行4个计数查询。这肯定会有所改善。

答案 3 :(得分:0)

如果数据库中有大量记录,则在值列上添加索引将加快SELECT。

上述帖子也提出了直接优化的一些优点。但是,随着您的数据库扩展,所有这些方法最终都会失败。由于分数是派生值,您可以将其缓存在Memcached,Redis甚至SQL中,这将确保在应用程序增长时获取分数在固定时间内进行缩放。您可以允许缓存过时并使用后台进程更新缓存。通过这样做,您的计算功能可以任意长,而不会影响用户体验。