我正在运行一个任务,以进口约100万个订单。我正在浏览数据以将其更新为新数据库上的值,并且使用RAM的8 gig
在本地计算机上正常工作。
但是,当我将其上传到我的AWS实例t2.medium
时,它将在前50万行中运行,但是到最后,我将在开始实际创建不存在的订单时开始最大限度地发出内存。我正在将mysql
数据库移植到postgres
我在这里错过了一些明显的东西吗?
require 'mysql2' # or require 'pg'
require 'active_record'
def legacy_database
@client ||= Mysql2::Client.new(Rails.configuration.database_configuration['legacy_production'])
end
desc "import legacy orders"
task orders: :environment do
orders = legacy_database.query("SELECT * FROM oc_order")
# init progressbar
progressbar = ProgressBar.create(:total => orders.count, :format => "%E, e[0;34m%t: |%B|e[0m")
orders.each do |order|
if [1, 2, 13, 14].include? order['order_status_id']
payment_method = "wx"
if order['paid_by'] == "Alipay"
payment_method = "ap"
elsif order['paid_by'] == "UnionPay"
payment_method = "up"
end
user_id = User.where(import_id: order['customer_id']).first
if user_id
user_id = user_id.id
end
order = Order.create(
# id: order['order_id'],
import_id: order['order_id'],
# user_id: order['customer_id'],
user_id: user_id,
receiver_name: order['payment_firstname'],
receiver_address: order['payment_address_1'],
created_at: order['date_added'],
updated_at: order['date_modified'],
paid_by: payment_method,
order_num: order['order_id']
)
#increment progress bar on each save
progressbar.increment
end
end
end
我假设此行orders = legacy_database.query("SELECT * FROM oc_order")
将整个表加载到内存,这是非常无效的。
您需要分批迭代桌子。在Activerecord中,有Find_each方法为此。您可能需要使用limit
和offset
实现自己的批次查询,因为您不使用ActivereCord。
为了有效地处理内存,您可以按批次查询运行MySQL查询,如> nattfodd 。
所建议的根据MySQL文档,有两种方法可以实现它:
SELECT * FROM oc_order LIMIT 5,10;
或者
SELECT * FROM oc_order LIMIT 10 OFFSET 5;
两个查询将返回6-15行。
您可以决定选择的偏移并在循环中运行查询,直到您的订单对象为空。
让我们假设您一次处理1000个订单,那么您将有类似的东西:
batch_size = 1000
offset = 0
loop do
orders = legacy_database.query("SELECT * FROM oc_order LIMIT #{batch_size} OFFSET #{offset}")
break unless orders.present?
offset += batch_size
orders.each do |order|
... # your logic of creating new model objects
end
end
还建议通过适当的错误处理在生产中运行您的代码:
begin
... # main logic
rescue => e
... # handle error
ensure
... # ensure
end
在订单集合中迭代时禁用行缓存应减少内存消耗:
orders.each(cache_rows: false) do |order|
有一个宝石可以帮助我们这样做,称为activerecord-import。
bulk_orders=[]
orders.each do |order|
order = Order.new(
# id: order['order_id'],
import_id: order['order_id'],
# user_id: order['customer_id'],
user_id: user_id,
receiver_name: order['payment_firstname'],
receiver_address: order['payment_address_1'],
created_at: order['date_added'],
updated_at: order['date_modified'],
paid_by: payment_method,
order_num: order['order_id']
)
end
Order.import bulk_orders, validate: false
使用单个插入语句。