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

我的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 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)