我最近开始为 Rails 应用程序的开发提供咨询和帮助,该应用程序使用 MongoDB(以 Mongoid 作为其 DB 客户端)来存储其所有模型实例。
这在应用程序处于早期启动阶段时很好,但随着应用程序获得越来越多的客户端并且开始需要越来越复杂的查询以在界面中显示正确的统计信息和其他信息,我们决定唯一可行的解决方案是规范化数据,并改为使用结构化数据库。
所以,我们现在正在将表和数据从 MongoDB(使用 Mongoid 作为对象映射器)迁移到 Postgres(使用 ActiveRecord 作为对象映射器)。因为我们必须确保 Mongo 数据库中没有不正确的非规范化数据,所以我们必须在 Rails-land 中运行这些数据迁移,以确保正在运行验证、回调和健全性检查。
开发过程中一切顺利,但现在我们正在使用真正的生产数据库在登台服务器上运行迁移。事实证明,对于某些迁移,服务器的内存使用量随着模型实例的数量线性增加,一旦我们填满了 16 GB 的 RAM(以及另外 16 GB 的交换空间......),就会导致迁移被终止。
由于我们一个一个地迁移模型实例,我们希望能够找到一种方法来确保内存使用可以保持(接近)恒定。
目前想到的可能导致这种情况的事情是 (a) ActiveRecord 或 Mongoid 保留对我们已经导入的对象实例的引用,以及 (b) 迁移在单个数据库事务中运行,因此 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
最佳答案
我建议通过执行所有读取但不执行任何模型创建/写入并查看内存使用量是否仍在增长,将内存消耗缩小到读取或写入方面(或者,换句话说,Mongoid vs AR)。
Mongoid 默认分批执行查找,不像 AR 必须通过 find_in_batches
请求。
由于 ActiveRecord 迁移默认包装在事务中,并且如果事务提交失败,AR 会执行属性值跟踪以将模型实例的属性恢复到之前的值,因此很可能正在创建的所有 AR 模型都保留在内存中,并且在迁移完成之前不能被垃圾收集。可能的解决方案是:
禁用相关迁移的隐式事务 (https://apidock.com/rails/ActiveRecord/Migration):
disable_ddl_transaction!
通过直接插入创建数据,完全绕过模型实例化(这也将加快进程)。最基本的方法是通过 SQL (Rails ActiveRecord: Getting the id of a raw insert),也有用于此的库 (Bulk Insert records into Active Record table)。
关于ruby-on-rails - 将 Rails 应用程序从 mongoid (MongoDB) 迁移到 ActiveRecord (Postgres) 时如何获得恒定的内存使用量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56377173/