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

解决导入大XLSX文件到数据库时的NoMemoryError内存分配失败问题

解决Ruby处理大XLSX文件时的NoMemoryError问题

这个内存溢出的坑我之前踩过好几次,本质是你用的Roo::Spreadsheet.parse会一次性把整个工作表的所有数据加载到内存里,遇到超大文件直接就撑爆了。咱们一步步来解决:

首选方案:逐行处理+批量插入(最稳妥)

不要一次性把所有数据读进内存,改成逐行读取、处理,再批量插入数据库,这样内存占用会保持在很低的水平。修改你的脚本如下:

file = 'lib/assets/sicepatfull.xlsx'
xlsx = Roo::Spreadsheet.open(file)
sheet = xlsx.sheet(0)

# 先处理表头,转成符合数据库字段的格式,同时排除:id
attrs = sheet.row(1).map { |k| k.parameterize.underscore.to_sym }.reject { |k| k == :id }

batch_size = 100 # 每100条数据批量插入一次,可根据你的服务器配置调整
records = []

# 从第2行开始遍历(第1行是表头)
(2..sheet.last_row).each do |row_num|
  row_data = sheet.row(row_num)
  # 把行数据和表头对应成哈希
  record_attrs = attrs.zip(row_data).to_h
  
  # 跳过空行(可选,根据你的数据情况决定是否保留)
  next if record_attrs.values.all?(&:nil?)
  
  records << record_attrs
  
  # 达到批量大小就插入数据库
  if records.size >= batch_size
    # insert_all!是Rails 6+的方法,速度快但不触发模型回调/验证;如果需要回调用Courier.create!(records)
    Courier.insert_all!(records)
    records = [] # 清空数组准备下一批
  end
end

# 插入最后一批剩余的记录
Courier.insert_all!(records) unless records.empty?

这个方案的好处是:

  • 每次只加载一行数据到内存,处理完就释放,不会积累大量数据
  • 批量插入减少了数据库交互次数,导入速度会快很多

备选方案:使用流式解析库(适合极大型文件)

如果你的XLSX文件大到离谱,Roo的逐行处理还是有点压力,可以试试Creek这个流式解析库——它不会把整个文件加载到内存,而是像读流一样逐行读取,内存占用极低。

首先安装gem:

gem install creek

然后修改脚本:

require 'creek'

file = 'lib/assets/sicepatfull.xlsx'
creek = Creek::Book.new(file)
sheet = creek.sheets.first

attrs = sheet.rows.first.map { |k| k.parameterize.underscore.to_sym }.reject { |k| k == :id }
batch_size = 100
records = []

# 跳过表头,遍历数据行
sheet.rows.drop(1).each do |row|
  row_data = row.values
  record_attrs = attrs.zip(row_data).to_h
  next if record_attrs.values.all?(&:nil?)
  
  records << record_attrs
  
  if records.size >= batch_size
    Courier.insert_all!(records)
    records = []
  end
end

Courier.insert_all!(records) unless records.empty?

应急方案:给Ruby分配更多内存

如果你的服务器有足够的物理内存,也可以临时给Ruby进程分配更多内存来应急,但这只是治标不治本的方法,不推荐作为长期解决方案。

运行脚本前先设置环境变量:

export RUBY_HEAP_MIN_SLOTS=1000000
export RUBY_HEAP_SLOTS_INCREMENT=1000000
export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
export RUBY_GC_MALLOC_LIMIT=1000000000
export RUBY_HEAP_FREE_MIN=500000

或者直接在运行脚本时指定:

ruby -e "ENV['RUBY_HEAP_MIN_SLOTS']='1000000'; ENV['RUBY_GC_MALLOC_LIMIT']='1000000000'; require './your_import_script.rb'"

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:36:59