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