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

Node.js出现EMFILE错误:加载超22万行SQLite3数据时文件打开过多

解决Node.js处理大量文件时的EMFILE错误

你遇到的这个问题很典型——虽然fs.readFile确实会自动关闭文件描述符,但你的代码一次性发起了22万多个异步读取请求,这远远超过了Mac OS X默认的文件描述符限制(默认一般是1024或更高,但肯定到不了22万)。forEach是同步遍历的,它会在瞬间把所有文件的读取请求都发出去,每个未完成的请求都会占用一个文件描述符,很快就把系统的配额耗尽了,所以才会抛出EMFILE: too many open files错误。

下面给你几个可行的解决方案,按推荐程度排序:

1. 控制异步操作的并发数(最推荐)

不要一次性发起所有请求,而是限制同时进行的文件读取/插入操作数量,比如一次只处理50个或者100个,这样就能把文件描述符的使用量控制在系统允许的范围内。

方案A:使用async库的eachLimit

先安装async库:

npm install async

然后修改代码:

const async = require('async');
const fs = require('fs');
const path = require('path');

const dir = '/your/target/directory';
const CONCURRENCY_LIMIT = 50; // 可根据机器性能调整

fs.readdir(dir, {}, (err, files) => {
  if (err) throw err;
  
  async.eachLimit(files, CONCURRENCY_LIMIT, (file, callback) => {
    fs.readFile(path.join(dir, file), 'utf8', (err, data) => {
      if (err) {
        console.error(`处理文件${file}出错:`, err);
        return callback(err);
      }
      // 处理文件并插入数据库的逻辑
      // ...
      callback(); // 告诉async这个文件处理完成了,可以处理下一个
    });
  }, (err) => {
    if (err) {
      console.error('批量处理出错:', err);
    } else {
      console.log('所有文件处理完成!');
    }
  });
});

方案B:用Promise+async/await实现限流(无需额外库)

如果不想引入第三方库,可以用原生的Promise来实现分批处理:

const fs = require('fs').promises;
const path = require('path');

const dir = '/your/target/directory';
const BATCH_SIZE = 50;

async function processFiles() {
  const files = await fs.readdir(dir);
  
  // 分批处理文件
  for (let i = 0; i < files.length; i += BATCH_SIZE) {
    const batch = files.slice(i, i + BATCH_SIZE);
    // 等待当前批次所有文件处理完成
    await Promise.all(batch.map(async (file) => {
      const data = await fs.readFile(path.join(dir, file), 'utf8');
      // 处理文件并插入数据库的逻辑
      // ...
    }));
    console.log(`已完成${Math.min(i + BATCH_SIZE, files.length)}/${files.length}个文件`);
  }
  
  console.log('所有文件处理完成!');
}

processFiles().catch(err => console.error('处理出错:', err));

2. 临时提高系统的文件描述符限制

Mac OS X默认的文件描述符上限比较低,你可以临时调高它,不过这只是治标不治本的方法(重启后会恢复默认):
在终端执行:

ulimit -n 65536

然后再运行你的Node.js程序。如果需要永久修改,需要修改系统配置文件,不过这个操作比较复杂,而且不推荐——因为即使调高了限制,一次性发起22万请求也会给系统带来很大压力,还是控制并发更合理。

3. 改用同步读取(不推荐)

虽然fs.readFileSync不会出现EMFILE问题(因为它读完一个文件才会处理下一个),但同步读取会阻塞Node.js的事件循环,处理22万文件会非常慢,而且期间程序无法响应其他请求,所以只适合小批量文件,不推荐用于你的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:05:53