【问题标题】:Rails efficiency on finding associationsRails 查找关联的效率
【发布时间】:2012-07-04 09:19:07
【问题描述】:

我有一个包含用户、帖子和评论的应用程序。用户有_很多帖子,帖子有_很多评论和评论属于_用户和帖子。

在我的视图模板中,我正在循环访问这样的评论以获取评论者的用户名:

User.find(comment.user_id).name

如果我为每个帖子的每个评论都这样做,效率如何? 我可以想象将用户名存储在 Comments 表的单独行中可能会更快,但复制数据似乎是错误的。

我是不是偏执狂,ActiveRecord 是在做一些缓存魔法,还是有更好的方法来做这样的事情?

【问题讨论】:

    标签: ruby-on-rails performance activerecord


    【解决方案1】:

    您可以在初始查询中预加载用户。 看到您在 User 和 Comment 之间设置了关联,您可以通过关联访问用户。

    假设你有一个控制器方法:

    def show
      @posts = Post.limit(10).includes(:comments => :user)
    end
    

    在您循环访问 @comments 时,您认为:

    @posts.each do |post|
      # show post stuff
      post.comments.each do |comment|
        comment.user.name
      end
    end
    

    【讨论】:

    • 所以,如果我限制返回的帖子(比如 10 个),@comments 只会抓取 10 个返回帖子的用户?
    • @daanVOS 我更新了我的示例。这会加载十个帖子,并热切地包括 cmets 和评论用户。
    • @daanVOS 这样一来,无论有多少 cmets,应用程序都可能会为帖子创建一个 SQL 查询,一个用于 cmets,一个用于创建这些 cmets 的用户。
    【解决方案2】:

    修改

    如果你有如下关联,你可以从评论对象本身comment.user访问用户

    class User < ActiveRecord::Base
      has_many :posts
      has_many :comments
    end
    
    class Post < ActiveRecord::Base
      belongs_to :user
      has_many :comments
    end
    
    class Comment < ActiveRecord::Base
      belongs_to :user
      belongs_to :post
    end
    

    【讨论】:

    • 这不是说评论用户与评论关联的帖子的所有者相同吗?在哪种情况下这是完全错误的,因为肯定有很多用户可以评论另一个用户的帖子?
    • @lebreeze,我也有同样的问题
    • @cutalion 我们的答案都非常相似,我几乎可以肯定它们是正确的。我不确定他们为什么被否决,因为这会误导那些真正想了解 Rails 关联的人。
    【解决方案3】:

    Eager loading of associations

    class Post < ActiveRecord::Base
      has_many :comments
      # ...
    end
    
    class Comment < ActiveRecord::Base
      belongs_to :user
    end
    
    
    comments = @post.comments.includes(:user)
    

    它将预加载用户。

    SQL 将如下所示:

    SELECT * FROM `comments` WHERE `comments`.`post_id` = 42;
    SELECT * FROM `users` WHERE `users`.`id` IN (1,2,3,4,5,6) # 1,2,3,4,5,6 - user_ids from comments
    

    【讨论】:

    • 您的意思是要加载带有 cmets 的帖子以及带有用户帖子的用户?
    【解决方案4】:

    如果性能对您来说是个问题,那么使用像 Mongodb 这样的非关系型数据库是不错的选择。

    如果您仍想使用 ActiveRecord,您可以使用带有 Post.comments.include(:user) 的即时加载(这将加载未使用的用户信息 - 这不是很好),或者您可以使用 缓存 技术。

    我发现可以按照您的建议将user.name 缓存在 cmets 表中,只要您控制可能发生的更改即可。您可以通过在 User 模型中设置回调来做到这一点:

    after_save do |user|
      Comment.where(user_id: user.id).update_all(:username, user.name)
    end
    

    这种技术有点像数据库缓存,但是,当然,您可以缓存 HTML 片段。注释块是缓存 HTML 块的好方法。

    【讨论】:

      猜你喜欢
      • 2011-08-18
      • 2015-02-20
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-08
      相关资源
      最近更新 更多