【问题标题】:Why is "<<" so much faster than "+=" for an ActiveRecord association when dealing with a many-to-many relationship?为什么在处理多对多关系时,ActiveRecord 关联的“<<”比“+=”快得多?
【发布时间】:2022-01-13 00:35:35
【问题描述】:
  • Rails v5.2.4.3
  • Ruby v2.3.3

我们有一个 Workspace 表和一个 WorkspaceGroup 表,这两个表之间的多对多关系通过一个名为 WorkspaceGroupAssociation 的连接表(工作区就像我们的域模型中的项目)。所以一个项目可以属于很多组,一个组可以有很多项目。

我们有一些团队有数千个项目,在我们的可观察性工具中,我们最近注意到以下旧代码非常慢(注意以下代码是该方法的简化版本):

class WorkspaceGroup < ApplicationRecord
  def add_workspaces(workspace_ids)
    self.workspace_ids |= workspace_ids
  end
end

我们有一个小组已经有大约 5,000 个工作区,添加这些新的工作区 ID 需要超过 2 分钟。

我们最初的方法是将self.workspace_ids |= workspace_ids 更改为self.workspace_ids += workspace_ids,但这在性能方面根本没有改变。然后我们尝试了以下方法,效果很好:

  def add_workspaces(workspace_ids)
    existing_workspaces = self.workspaces
    workspaces_to_add = Workspace.where(id: workspace_ids) - existing_workspaces
    workspaces_to_add.each do |workspace|
      self.workspaces << workspace
    end
  end

上述代码的作者说性能提升是因为我们没有在新代码中实例化 5,000 个 Workspace 模型的新实例,而是在旧代码中。

我很好奇为什么旧代码是这样,而新代码却不是。为什么self.workspace_ids += 会导致实例化数千个新的 ActiveRecord 实例,而self.workspaces &lt;&lt; 不会?

【问题讨论】:

    标签: ruby-on-rails rails-activerecord


    【解决方案1】:

    + 为 Rails 中的集合执行此操作

    def +(other)
      Collection.new(to_a + other.to_a)
    end
    

    虽然&lt;&lt; 这样做...

    def <<(*records)
      proxy_association.concat(records) && self
    end
    

    我的猜测是,创建一个新的 Collection 比进行串联操作更昂贵。


    https://api.rubyonrails.org/v6.1.4/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-3C-3C

    https://api.rubyonrails.org/classes/Rails/Initializable/Collection.html#method-i-2B

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多