Ruby不释放内存
我的Ruby代码大致如下所示Ruby不释放内存,ruby,memory,ruby-2.3,Ruby,Memory,Ruby 2.3,我的Ruby代码大致如下所示 offset = 0 index = 1 User.establish_connection(..) # db1 class Member < ActiveRecord::Base self.table_name = 'users' end Member.establish_connection(..) #db2 while true users = User.limit(10000).offset(offset).as_json ## for
offset = 0
index = 1
User.establish_connection(..) # db1
class Member < ActiveRecord::Base
self.table_name = 'users'
end
Member.establish_connection(..) #db2
while true
users = User.limit(10000).offset(offset).as_json ## for a Database 1
offset = limit * index
index += 1
users.each do |u|
member = Member.find_by(name: u[:name])
if member.nil?
Member.create(u)
elsif member.updated_at < u[:updated_at]
member.update_attributes(u)
end
end
break if break_condition
end
在Ruby版本2.2.2和2.3.0上进行了测试
编辑:其他详细信息
1操作系统
2通过rvm安装并遵守ruby
3 ActiveRecord 4.2.6版我无法告诉您内存泄漏的来源,但我确实发现了一些不太可能解决的问题 但首先,有两件事: 您确定ActiveRecord是将数据从一个数据库复制到另一个数据库的正确方法吗?我很有信心不是这样的。每个主要的数据库产品都有强大的导出和导入功能,您将看到它的性能比在Ruby中的性能好很多很多倍,而且您始终可以在应用程序中调用这些工具。在你继续走这条路之前,好好想一想 10000这个数字是从哪里来的?您的代码表明,您知道一次获取所有记录不是一个好主意,但10000条记录仍然很多。你可以通过简单地尝试不同的数字看到一些收益:比如100或1000 也就是说,让我们深入了解这一行的作用:
users = User.limit(10000).offset(offset).as_json
第一部分User.limit10000.offsetoffset创建一个表示查询的ActiveRecord::Relation对象。当您对其调用as_json时,将执行查询,该查询实例化10000个用户模型对象并将它们放入一个数组中,然后根据这些用户对象的每个属性构造一个哈希。看看ActiveRecord::Relationas_json的源代码
换句话说,实例化10000个用户对象只是为了在获得它们的属性后将它们扔掉
所以,一个快速的胜利就是完全跳过这一部分。只需选择原始数据:
user_keys = User.attribute_names
until break_condition
# ...
users_values = User.limit(10000).offset(offset).pluck(user_keys)
users_values.each do |vals|
user_attrs = user_keys.zip(vals).to_h
member = Member.find_by(name: user_attrs["name"])
member.update_attributes(user_attrs)
end
end
返回一个数组,其中包含每个记录的值。在user_values.each循环中,我们将该值数组转换为散列。不需要实例化任何用户对象
现在让我们来看看这个:
member = Member.find_by(name: user_attrs["name"])
member.update_attributes(user_attrs)
这将从数据库中选择一条记录,实例化一个成员对象,然后在while循环的每次迭代中更新数据库中的记录10000次。如果需要在更新记录时运行验证,这是正确的方法。但是,如果不需要运行验证,也可以通过不实例化任何对象来节省时间和内存:
Member.where(name: user_attrs["name"]).update_all(user_attrs)
区别在于,它不会从数据库中选择记录或实例化成员对象,而只是更新它。您在上面的评论中说,name列上有一个唯一的约束,因此我们知道这将只更新一条记录
在进行了这些更改之后,您仍然必须面对这样一个事实,即在while循环的每个迭代中必须执行10000个更新查询。再次,考虑使用数据库的内置导出和导入功能,而不是试图让Rails这样做。你是说while?或多或少看起来像这样,也许最好显示准确的代码?@fl00r它是准确的代码,除了类或模型名是changed@matt是的,那是一个typo@niceman在生产模式下运行时,没有太多时间来测试评测内容。如果我有时间,我会分享结果。谢谢你的回答。很抱歉,复制到不同的数据库不是很直接,因此不能使用pg_导入和pg_转储。我已经更新了代码,以显示复制是如何工作的。不过,还有更好的方法可以做到这一点。你基本上是在做一个简单的条件更新。如果数据位于同一数据库中的两个单独的表中,则可以使用相同的条件进行连接,以获得要追加的行。由于它们不在同一个数据库中,您可以使用不同的名称导出并导入到表中,也可以使用它们直接连接到另一个数据库。
member = Member.find_by(name: user_attrs["name"])
member.update_attributes(user_attrs)
Member.where(name: user_attrs["name"]).update_all(user_attrs)