如何通过NodeJS API导出MongoDB全量集合数据为CSV(内存溢出问题)
解决MongoDB全量导出CSV时Node.js堆内存溢出问题
这个问题太典型了——一次性把50多万条文档全加载到内存里,直接把Node.js的堆内存给撑爆了。咱们换个思路,用流式处理分批读取数据,不用一次性把所有数据都存到内存里,就能完美解决溢出问题。
问题根源
你的原代码里Collection.find({}).then(logs => ...)会让MongoDB驱动把所有查询结果一次性转换成JavaScript对象数组,全部塞进Node.js的堆内存。50万条数据的内存占用远超Node.js默认的堆内存限制(通常在1.4GB左右),自然触发了内存溢出错误。
解决方案:游标+流式处理
MongoDB驱动支持游标(cursor),可以分批从数据库拉取数据,配合Node.js的流API,我们能做到边读数据、边转CSV、边发响应,全程不会把所有数据都存到内存里。
修改后的代码示例:
const { Readable } = require('stream'); // 先设置响应头(和原来逻辑一致) res.charset = 'utf-8'; res.setHeader('Content-disposition', 'attachment; filename=filename.csv'); res.setHeader('content-type', 'text/csv'); // 获取MongoDB游标,替代一次性拉取全量数据 const cursor = Collection.find({}).cursor(); // 将游标转换为可读流(部分MongoDB驱动版本可直接用cursor作为流,这里兼容处理) const dataStream = Readable.from(cursor); // 配置CSV转换规则,保留你的字段映射逻辑 const csvStream = csv.format({ headers: true, transform: function(row) { // 注意处理可能的null/undefined字段,避免转换报错 return { 'Nivel': row.level || '', 'Metodo': row.method || '', 'IP': row.ip || '', 'URL': row.url || '', 'Status Code': row['status-code'] || '', 'Usuario': row.user_email?.email || '', // 用可选链处理嵌套字段的空值 'Fecha': row.createdAt || '', 'Cuerpo': row.req_body || '', 'Consulta': row.req_query || '', 'Navegador': row['user-agent'] || '', 'Datos Usuario': JSON.stringify(row.user_email) || '', // 对象转字符串避免CSV格式混乱 'Aplicacion': row.referrer || '' }; } }); // 连接流管道:游标数据流 → CSV转换流 → 响应输出流 dataStream.pipe(csvStream).pipe(res); // 处理流的异常情况 dataStream.on('error', (err) => { console.error('读取MongoDB数据出错:', err); res.status(500).send('CSV导出失败'); }); csvStream.on('error', (err) => { console.error('CSV格式转换出错:', err); res.status(500).send('CSV导出失败'); }); res.on('finish', () => { console.log('全量数据CSV导出完成'); });
额外优化建议
- 自定义分批大小:可以给游标设置
batchSize参数,控制每次从数据库拉取的文档数量,比如Collection.find({}).cursor({ batchSize: 2000 }),默认是1000,你可以根据服务器内存情况调整。 - 字段过滤:如果不需要导出所有字段,在
find()里指定需要的字段,减少单条文档的内存占用,比如Collection.find({}, { level: 1, method: 1, ip: 1 })。 - 临时内存扩容(非首选):如果只是临时应急,可以启动Node.js时增加堆内存限制,比如
node --max-old-space-size=4096 your-api-script.js(设置4GB堆内存),但这只是治标不治本,流式处理才是长期解决方案。
内容的提问来源于stack exchange,提问作者oihi08




