将Rails应用程序从Mongoid(MongoDB)迁移到ActiveRecord(Postgres)时,如何获得恒定的内存使用率?

时间:2019-05-30 11:12:42

标签: ruby-on-rails mongodb postgresql activerecord mongoid

我最近开始咨询和帮助开发Rails应用程序,该应用程序使用MongoDB(以Mongoid作为其数据库客户端)来存储其所有模型实例。

这在应用程序处于早期启动阶段是可以的,但是随着应用程序拥有越来越多的客户端,并且在开始需要越来越复杂的查询以在界面中显示适当的统计信息和其他信息时,我们决定唯一可行的解​​决方案是标准化数据,然后移至结构化数据库。

因此,我们现在正在将表和数据都从MongoDB(以Mongoid作为对象映射器)迁移到Postgres(以ActiveRecord作为对象映射器)。因为必须确保Mongo数据库中没有不正确的非规范化数据,所以我们必须在Rails-land内部运行这些数据迁移,以确保正在运行验证,回调和健全性检查。

所有开发工作都进行得很好,但是现在我们在带有真实生产数据库的登台服务器上运行迁移。事实证明,对于某些迁移,服务器的内存使用量会随着模型实例数量的增加而线性增加,一旦我们填满16 GB的RAM(以及另外16GB的交换空间...),就会导致迁移被终止。 / p>

由于我们逐个迁移了模型实例,因此我们希望能够找到一种方法来确保内存使用保持不变(接近)。

当前可能导致此问题的原因是(a)ActiveRecord或Mongoid保持对我们已经导入的对象实例的引用,以及(b)迁移是在单个DB事务中运行的,因此Postgres需要更多还有更多的内存,直到完成为止?

所以我的问题:

  • 这种线性内存使用的可能原因是什么?
  • 我们如何减少它?
  • 有没有办法使Mongoid和/或ActiveRecord放弃旧的引用?
  • 我们应该尝试手动调用Ruby GC吗?
  • 有没有办法将数据迁移分为多个数据库事务,这会有所帮助吗?

这些数据迁移具有以下格式:

class MigrateSomeThing < ActiveRecord::Migration[5.2]
  def up
    Mongodb::ModelName.all.each do |old_thing| # Mongoid's #.all.each works with batches, see https://stackoverflow.com/questions/7041224/finding-mongodb-records-in-batches-using-mongoid-ruby-adapter 
      create_thing(old_thing, Postgres::ModelName.new)
    end
    raise "Not all rows could be imported" if MongoDB::ModelName.count != Postgres::ModelName.count
  end

  def down
    Postgres::ModelName.delete_all
  end

  def create_thing(old_thing, new_thing)
    attrs = old_thing.attributes
    # ... maybe alter the attributes slightly to fit Postgres depending on the thing.
    new_thing.attributes = attrs
    new_thing.save!
  end

end

1 个答案:

答案 0 :(得分:1)

我建议通过执行所有读取但不创建/写入任何模型,并查看内存使用量是否仍在增长,来将内存消耗范围缩小到读取或写入端(或者换句话说,Mongoid与AR)。

Mongoid默认执行分批查找,与AR不同,AR必须通过find_in_batches请求。

由于ActiveRecord迁移默认情况下包装在事务中,并且AR会执行属性值跟踪,以在事务提交失败时将模型实例的属性恢复为先前的值,因此所有正在创建的AR模型都可能保留在内存中,并且在迁移完成之前无法进行垃圾收集。可能的解决方案是:

  1. 为有问题的迁移(https://apidock.com/rails/ActiveRecord/Migration)禁用隐式事务:

    disable_ddl_transaction!

  2. 通过直接插入创建数据,完全绕过模型实例化(这还将加快过程)。最基本的方法是通过SQL(Rails ActiveRecord: Getting the id of a raw insert),也有用于此的库(Bulk Insert records into Active Record table)。