为什么在处理多对多关系时,对于 ActiveRecord 关联,“<<”比“+=”快得多?
Why is "<<" so much faster than "+=" for an ActiveRecord association when dealing with a many-to-many relationship?
- Rails v5.2.4.3
- Ruby v2.3.3
我们有一个工作区 table 和一个工作区组 table,这两个 table 之间的多对多关系通过一个名为 WorkspaceGroupAssociation 的连接 table (工作区就像我们领域模型中的项目)。所以一个项目可以属于多个组,一个组可以有多个项目。
我们有一些团队有数千个项目,在我们的可观察性工具中,我们最近注意到以下旧代码非常慢(注意以下代码是该方法的简化版本):
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 <<
却不会?
+
为 Rails
中的集合执行此操作
def +(other)
Collection.new(to_a + other.to_a)
end
虽然 <<
这样做...
def <<(*records)
proxy_association.concat(records) && self
end
我的猜测是,创建一个新的 Collection
是一个比串联更昂贵的操作。
https://api.rubyonrails.org/classes/Rails/Initializable/Collection.html#method-i-2B
- Rails v5.2.4.3
- Ruby v2.3.3
我们有一个工作区 table 和一个工作区组 table,这两个 table 之间的多对多关系通过一个名为 WorkspaceGroupAssociation 的连接 table (工作区就像我们领域模型中的项目)。所以一个项目可以属于多个组,一个组可以有多个项目。
我们有一些团队有数千个项目,在我们的可观察性工具中,我们最近注意到以下旧代码非常慢(注意以下代码是该方法的简化版本):
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 <<
却不会?
+
为 Rails
def +(other)
Collection.new(to_a + other.to_a)
end
虽然 <<
这样做...
def <<(*records)
proxy_association.concat(records) && self
end
我的猜测是,创建一个新的 Collection
是一个比串联更昂贵的操作。
https://api.rubyonrails.org/classes/Rails/Initializable/Collection.html#method-i-2B