2012-03-16 45 views
0

迁移包含以下内容:内存泄漏3.0.11迁移

Service.find_by_sql("select 
         service_id, 
         registrations.regulator_given_id, 
         registrations.regulator_id 
        from 
         registrations 
        order by 
         service_id, updated_at desc").each do |s| 
    this_service_id = s["service_id"] 
    if this_service_id != last_service_id 
    Service.find(this_service_id).update_attributes!(:regulator_id => s["regulator_id"],      
        :regulator_given_id => s["regulator_given_id"]) 
    last_service_id = this_service_id 
    end 
end 

,它是吃了记忆,在那里它不会在允许的Heroku 512MB的运行点(注册表具有60,000项)。有没有已知的问题?解决方法?修复Rails的更高版本?

提前感谢以下要求澄清

编辑:
这是所有相关的源 - 迁移其余创建正在填充的两个新列。这种情况是我在注册表中拥有来自多个来源(服务的监管机构)的服务数据。我已决定将一些数据([prime] regulator_id和[prime] regulator_given_key)提升到主要监管机构的服务表中,以加速某些查询。

+0

这不是内存泄漏,只是一个使用大量内存的情况,因为'.each'之前的所有内容都会导致Rails在开始迭代之前加载60K行。你能否用文字描述你试图解决的问题(编辑问题,离开源代码);我确信有一个简单的解决方案。 – 2012-03-16 12:58:17

+0

它看起来像你试图从'registrations'到'services'复制两列,假设在现有的'registrations'中,'regulator_given_id'和'regulator_id'对于任何给定的'service_id'是相同的。 (我假设一旦完成,你可以从注册中删除这两列,并更新':belongs_to'和':has_many'模型声明)。如果这是正确的,那么有一个简单的解决方案。 – 2012-03-16 13:12:19

+0

是的你是对的。我试图想到一个简单的解决方案,但没有提出一个解决方案。我没有提到的一件事是,可能有多个注册服务,我只想要最近的(只是为了防止对你正与我分享的精美优雅的快捷方式的影响)。 – baldmark 2012-03-16 13:19:10

回答

0

这将一次加载所有60000个项目并将这些60000个AR对象保留在周围,这将消耗相当数量的内存。 Rails确实提供了一个find_each方法,用于一次将这样的查询分解为1000个对象的块,但它不允许您像指定的那样指定排序。

您可能最好实施自己的分页方案。使用限制/偏移量是可能的,但是大的OFFSET值通常效率不高,因为数据库服务器必须生成一堆结果,然后丢弃。

另一种方法是向查询添加条件,以确保您不返回已处理的项目,例如指定service_id小于先前返回的值。如果在这个问题上比较一些项目是平等的,这更加复杂。使用这两种分页类型方案时,您可能需要考虑如果在处理它时将行插入到注册表中会发生什么情况(可能不是迁移问题,假设您在访问禁用的网站的情况下运行它们)

+0

尽管您的策略完全有可能帮助我解决问题(并且最终可能会使用它们),但我相当确定存在泄漏,因为内存使用量是渐进的 - 它增加到7MB或一秒钟而它正在运行更新位。 – baldmark 2012-03-16 13:17:08

0

(注:OP报告并没有工作)

尝试是这样的:

previous = nil 
Registration.select('service_id, regulator_id, regulator_given_id') 
    .order('service_id, updated_at DESC') 
    .each do |r| 
    if previous != r.service_id 
    service = Service.find r.service_id 
    service.update_attributes(:regulator_id => r.regulator_id, :regulator_given_id => r.regulator_given_id) 
    previous = r.service_id 
    end 
end 

这是一种从regulators得到最新记录的哈克的方式 - 有无疑是一个更好的在SQL中使用DISTINCTGROUP BY的方法全部在单个队列中这不仅会快得多,而且会更加优雅。但这只是一种迁移,对吧?我没有保证优雅。我也不知道它会工作和解决问题,但我认为所以:-)

关键的变化是,而不是使用SQL,这使用AREL,这意味着(我认为)更新操作执行一旦在每个相关记录上,AREL将它们返回。使用SQL,你将它们全部返回并存储在一个数组中,然后全部更新它们。我也不认为有必要使用.select(...)条款。

对结果非常感兴趣,所以让我知道它是否有效!

+0

这看起来与我所做的完全相同,并且它看起来具有相同的性能并使用了更多的内存。我猜想update_attributes中有内存泄漏。该网站不完全是Twitter - 我可以复制数据,运行迁移并将其复制回来,无需任何人注意我是否选择了我的时刻。 – baldmark 2012-03-16 17:45:54

+0

嗯,我想我有一个错误的概念,但正如你所说,我错了。有'find_each'和'find_in_batches'方法http://apidock.com/rails/ActiveRecord/Batches/find_each这似乎旨在处理你正在遇到的问题。如果'update_attributes'有内存泄漏,我会感到非常惊讶 - 它所做的只是调用'save'。您也可以查看'update_attribute',它只影响一列,但不会调用验证回调,因此执行其中的两个实际上可能会更快。 – 2012-03-16 18:21:12