You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

处理50万条记录致服务器崩溃,如何分批处理?

优化大规模数据处理Rake任务的方案

你的问题核心在于一次性加载50万条记录到内存,加上Rails默认的查询缓存、未及时释放的数据库连接,最终导致内存和CPU耗尽。下面是几个无需依赖Sidekiq的可行优化方案,按优先级排序:

1. 强制分批加载记录(必做)

Rails的each会把所有符合条件的记录一次性实例化为ActiveRecord对象,直接占满内存。改用find_each或find_in_batches,它们会自动分批查询并释放内存:

task get_new_data: :environment do
  # 关闭查询缓存,避免批量处理时缓存无效数据占用内存
  Model.uncached do
    # 按1000条一批处理,可根据服务器配置调整大小
    Model.where('column IS NOT NULL').order('id DESC').find_each(batch_size: 1000) do |d|
      begin
        puts "...processing record #{d.id}..."
        $stdout.flush # 强制刷新终端输出,解决前期无内容的问题

        api_call = API::Call(...)
        d.assign_attributes(attr1: api_call.data1, attr2: api_call.data2)
        d.save!(validate: false)

        puts "...record #{d.id} updated..."
        $stdout.flush

        # 随机sleep,避免固定间隔导致的API/服务器压力集中
        sleep(0.3 + rand(0.4))
      rescue => e
        # 记录错误,避免单条失败中断整个任务
        Rails.logger.error "Failed to process record #{d.id}: #{e.message}"
        next
      end
    end
  end
end

2. 释放数据库连接(必做)

如果任务运行时间很长,ActiveRecord会一直占用数据库连接,可能导致连接池耗尽。每处理完一批后手动释放连接:

Model.where('column IS NOT NULL').order('id DESC').find_in_batches(batch_size: 1000) do |batch|
  batch.each do |d|
    # 处理逻辑...
  end
  # 释放当前线程的数据库连接
  ActiveRecord::Base.clear_active_connections!
end

3. 批量更新优化(可选)

如果API返回的数据可以批量获取,或者更新逻辑无依赖,可以用update_all减少SQL查询次数。比如先收集一批需要更新的id和对应数据,再批量执行:

Model.where('column IS NOT NULL').order('id DESC').find_in_batches(batch_size: 2000) do |batch|
  updates = []
  batch.each do |d|
    api_call = API::Call(...)
    updates << { id: d.id, attr1: api_call.data1, attr2: api_call.data2 }
  end
  # 批量生成UPDATE语句,减少数据库交互
  updates.each_slice(100) do |slice|
    Model.transaction do
      slice.each do |update|
        Model.where(id: update[:id]).update_all(attr1: update[:attr1], attr2: update[:attr2])
      end
    end
  end
  ActiveRecord::Base.clear_active_connections!
  sleep(1) # 批量处理后适当延长休息时间
end

4. 服务器资源保护(可选)

如果服务器配置较低,可以限制任务的CPU使用,比如用nice命令启动Rake任务,降低进程优先级:

nice -n 10 bundle exec rake get_new_data

原任务崩溃的核心原因

  • 一次性加载50万条ActiveRecord对象,内存占用随处理持续增长,最终耗尽
  • Ruby的stdout默认缓冲,导致前期无输出,给人任务卡住的错觉
  • 长时间运行未释放数据库连接,连接池耗尽后数据库响应变慢,加剧CPU占用

内容的提问来源于stack exchange,提问作者user984621

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 11:50:09