使用activerecord-import批量导入CSV并更新重复值时出现内存不足报错
问题解决方案
你遇到的PG内存溢出错误核心原因是单次导入全量180万条记录,单条SQL的体积超出了PG的内存分配上限,和你调整的重复更新字段配置无关,按以下优先级优化即可解决:
1. 优先做分批导入
不要把所有CSV行一次性存入数组后全量导入,每攒1000~5000行就调用一次import,同时避免全量数据常驻应用内存:
BATCH_SIZE = 3000 converter = lambda { |header| header.downcase } Zip::File.open(content) do |zip| zip.each do |entry| next unless final === entry.name data = zip.read(entry) batch = [] CSV.parse(data, headers: true, header_converters: converter, encoding: 'ISO-8859-1') do |row| batch << row.to_h if batch.size >= BATCH_SIZE Carrier.import( batch, on_duplicate_key_update: {conflict_target: [:dot_number], columns: [:mcs150_date]}, validate: false # 确认CSV数据合规的情况下一定要加,大幅提升性能 ) batch.clear end end # 处理最后一批不足BATCH_SIZE的记录 Carrier.import( batch, on_duplicate_key_update: {conflict_target: [:dot_number], columns: [:mcs150_date]}, validate: false ) if batch.any? end end
2. 提前过滤完全重复的记录
你提到大部分记录是完全重复无需处理的,先预查已有数据筛掉无效记录,能把实际导入量从180万压缩到几万级别:
- 先从CSV里提取所有
dot_number和对应的mcs150_date,存在临时哈希里 - 批量查询Carrier表中已存在的这些
dot_number的mcs150_date,和临时哈希对比 - 只保留日期不一致的待更新记录、和不存在的待新增记录,再走分批导入
3. 可选优化项
- 导入前临时删除Carrier表的非唯一索引,导入完成后再重建,能减少导入时的索引维护开销
- 若CSV数据来源可靠,可添加
recursive: false、timestamps: false(如果不需要自动更新更新时间的话)参数,进一步降低activerecord-import的额外开销
内容的提问来源于stack exchange,提问作者AttemptedMastery
相关产品推荐
相关产品推荐

