Rails:如何最大限度地减少数据库命中?急切加载不适用

时间:2009-06-26 09:55:47

标签: ruby-on-rails performance session caching eager-loading

这个问题可能有点具体,但我认为一般的pov也很有趣。

在Rails App中,用户可以订阅其他用户。当我显示我必须检查的用户列表时,如果当前用户已订阅列表中的用户。如果他订阅了,我会显示取消订阅按钮,反之亦然。

因为整个事情取决于当前用户我不能使用急切加载。因此,当我在列表中显示20个用户时,我会在数据库上生成20个额外的点击,这在我看来是不好的做法。

我正在考虑解决这个问题的好方法。到目前为止,我提出的最佳解决方案是在登录期间加载current_user在会话中订阅的用户的ID,然后只检查每个user.id与会话中的ID。但是,当用户订阅了很多人时,这可能会导致其他问题。此外,我不确定这是否是加载所有订阅的最佳方式,即使用户在此会话期间可能永远不会查看用户列表。

我想到的最好的事情是做同样的事情,但不是在登录时,而是在加载用户列表时。

您怎么看?

2 个答案:

答案 0 :(得分:4)

您绝对应该开始使用缓存系统。 您可以遵循至少3种方式。您也可以将它们结合起来以提高效率。

数据库缓存

创建一个关系表来保存用户和订阅者之间的ID关系,这样您就不需要动态计算它们。

模型缓存

可以缓存昂贵的查询。

def find_subscribers
  Rails.cache.fetch("find_subscribers_#{current_user}") do
    # run the query
  end
end

查看缓存

您还可以缓存视图片段,以防止进行昂贵的细化。

您可能希望从以下开始:

修改

您可以优化查询。

ActiveRecord::Base.connection.execute("SELECT count(*) as c FROM subscribers_users WHERE user_id = #{other_user.id} AND subscriber_id = #{self.id}") 

可以成为

counters = SubscribersUser.count(:conditions => { :subscriber_id => self.id }, :group => "user_id")

查询将返回一个Hash,其中键是user_id,值是count的结果。然后,您可以迭代哈希,而不是为视图中的任何记录运行查询。

答案 1 :(得分:2)

如果允许您使用预先加载,则没有规则可以重新加载当前用户:

user = User.find(current_user.id,:include =>:subscriber)

如果这是一个简单的has_many关系,那么这只是两个SQL语句。

现在,您可以迭代user.subscribers而不会产生另一个数据库命中。