Node.js处理超200万条记录数组时函数失效问题排查
问题分析与优化方案
看起来你遇到了典型的大数据量下的性能瓶颈,代码卡在result生成这一步完全是时间复杂度和内存占用超标导致的,咱们一步步拆解问题:
核心问题原因
- O(n²)的爆炸级时间复杂度:你在
uniqNumber.map里嵌套了concatArrays.filter——每遍历一个唯一ID,就要把200万+条记录的数组全扫一遍。200万的量级下,这就是200万×200万=4e12次操作,JS单线程根本扛不住,直接卡死是必然的。 - 不必要的内存浪费:
concatArrays把两个超大数组合并成一个,本身就占用了大量内存;uniqNumber生成时遍历一次大数组,生成result又反复遍历,内存和CPU双重过载。 - 重复的无效计算:每次
parseInt(el[0])和parseInt(number)都是重复类型转换,额外消耗性能。
优化后的代码方案
咱们用Map数据结构做分组,只需要遍历两次原数组(甚至不用合并成大数组),时间复杂度直接降到O(n),内存占用也会大幅降低:
const fs = require('fs'); const iconv = require('iconv-lite'); // 假设你使用的是常用的iconv-lite库 const _ = require('lodash'); // 处理第一个CSV:提前转换ID为数字,过滤无效行 const arr1 = fs.readFileSync('./csv1.csv', 'utf-8') .toString() .split('\n') .map(row => { const parts = row.split(';'); const id = parseInt(parts[0]); return [id, parts]; // 提前存好转换后的ID,避免重复计算 }) .filter(item => !isNaN(item[0])); // 过滤ID无效的空行/错误行 // 处理第二个CSV:同样提前转换ID,过滤无效行 const arr2 = iconv.encode( iconv.decode(fs.readFileSync('./csv2.csv'), "win1251"), "utf8" ) .toString() .split('\n') .map(row => { const parts = row.split(';'); const id = parseInt(parts[0]); return [id, parts]; }) .filter(item => !isNaN(item[0])); // 用Map做分组,一次遍历完成归类 const groupMap = new Map(); // 批量处理arr1 for (const [id, row] of arr1) { if (!groupMap.has(id)) { groupMap.set(id, []); } groupMap.get(id).push(row); } // 批量处理arr2 for (const [id, row] of arr2) { if (!groupMap.has(id)) { groupMap.set(id, []); } groupMap.get(id).push(row); } // 转换成你需要的result格式 const result = Array.from(groupMap.entries());
额外优化建议
- 避免一次性读取大文件:如果CSV文件特别大,
fs.readFileSync会把整个文件加载到内存,可能直接触发内存溢出。可以改用fs.createReadStream逐行读取处理,进一步降低内存压力。 - 去掉冗余的Lodash调用:用Map分组后自动完成了ID去重,不需要再调用
_.uniq生成uniqNumber数组,又省了一次全量遍历。 - 处理换行符边界:注意
split('\n')可能会产生最后一个空元素,提前过滤掉能减少不必要的处理。
这样改完后,处理200万条数据应该几秒就能完成,不会再出现冻结的情况了。
内容的提问来源于stack exchange,提问作者ravil
相关产品推荐
相关产品推荐

