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

Node.js导入超大JSON文件至MongoDB报错解决方案

实现方案

原有代码报错原因

  • 异步逻辑缺失:newLink.save()为异步操作,循环中未等待写入完成就发起下一次请求,75万条无并发控制的请求会直接打满数据库连接,甚至在库、集合未完成初始化时就发起写入,触发建库失败、连接超时问题。
  • 数据类型不匹配:通过require()加载JSON文件得到的是完整URL数组,并非单条字符串,直接将数组赋值给link字段不符合Schema字段类型要求,必然触发校验错误。
  • 写入效率极低:单条循环调用save()的网络IO开销是批量写入的上百倍,75万条数据量级下极易中途出现内存溢出、连接断连问题。

前置配置

首先定义Schema并给URL字段添加唯一索引,从根源避免重复数据,同时大幅提升后续查询、校验速度:

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

// 定义诈骗URL集合Schema
const ScamLinkSchema = new mongoose.Schema({
  link: { type: String, required: true, unique: true, trim: true },
  createdAt: { type: Date, default: Date.now }
});
const Rescamdb = mongoose.model('rescamdb', ScamLinkSchema);

75万条URL高效导入实现

采用分批插入策略,单批大小控制在2000条,避免超过MongoDB单文档16M大小限制,同时通过ordered:false配置跳过重复数据,不中断整体导入流程:

async function batchImportScamLinks() {
  // 等待数据库连接成功后再执行后续操作
  await mongoose.connect('mongodb://127.0.0.1:27017/你的目标库名', {
    maxPoolSize: 20 // 控制连接池大小,避免连接数溢出
  });
  console.log('数据库连接成功,初始化索引');
  // 提前创建索引,完成后再导数据
  await Rescamdb.init();

  const files = fs.readdirSync(path.join(__dirname, './files'));
  for (const file of files) {
    // 用fs读取JSON替代require,避免require缓存问题、大文件加载异常
    const fileContent = fs.readFileSync(path.join(__dirname, './files', file), 'utf8');
    const linkArr = JSON.parse(fileContent);
    console.log(`加载文件${file},共读取${linkArr.length}条URL,开始分批导入`);

    const batchSize = 2000;
    for (let i = 0; i < linkArr.length; i += batchSize) {
      // 组装当前批次数据
      const currentBatch = linkArr.slice(i, i + batchSize).map(link => ({
        link: link.trim()
      }));
      try {
        await Rescamdb.insertMany(currentBatch, { ordered: false });
        console.log(`导入进度:${Math.min(i + batchSize, linkArr.length)}/${linkArr.length}`);
      } catch (err) {
        // 仅忽略重复键错误,其余错误打印排查
        if (err.code !== 11000) console.error(`批次${i}导入异常`, err);
      }
    }
  }
  console.log('全部数据导入完成');
}

batchImportScamLinks().catch(err => {
  console.error('导入任务失败', err);
  process.exit(1);
});

提示:如果单份JSON文件大小超过100M,替换为流式JSON解析工具做逐段读取即可,75万条纯URL总大小通常在15M左右,直接读取不会有内存压力。

跨库URL不匹配校验实现

采用Set做内存比对避免频繁查库,分批拉取业务库数据控制内存占用,校验速度比单条查询快100倍以上:

async function verifyUrlMatch() {
  // 分别连接两个目标数据库
  const scamDbConn = mongoose.createConnection('mongodb://127.0.0.1:27017/诈骗URL存储库');
  const bizDbConn = mongoose.createConnection('mongodb://127.0.0.1:27017/待校验业务库');

  const ScamLinkModel = scamDbConn.model('rescamdb', ScamLinkSchema);
  // 替换为待校验库的集合结构
  const BizLinkModel = bizDbConn.model('你的业务集合名', new mongoose.Schema({
    url: { type: String, trim: true }
  }));

  // 仅拉取URL字段,减少不必要的内存开销
  const allScamLinks = await ScamLinkModel.find({}, { link: 1, _id: 0 }).lean();
  const scamLinkSet = new Set(allScamLinks.map(item => item.link));

  const batchSize = 5000;
  let offset = 0;
  const mismatchResult = [];
  while (true) {
    // 分批拉取业务库URL
    const bizLinks = await BizLinkModel.find({}, { url: 1, _id: 0 })
      .skip(offset)
      .limit(batchSize)
      .lean();
    if (bizLinks.length === 0) break;
    for (const item of bizLinks) {
      // 比对逻辑可按实际需求调整:以下逻辑为业务库URL不在诈骗库中则判定为不匹配
      if (item.url && !scamLinkSet.has(item.url)) {
        mismatchResult.push(item.url);
      }
    }
    offset += batchSize;
    console.log(`校验进度:已完成${offset}条数据检查`);
  }
  console.log(`校验完成,共发现${mismatchResult.length}条不匹配URL`);
  // 可按需将mismatchResult写入文件或存储到指定集合
}

// 导入完成后再执行校验
// batchImportScamLinks().then(() => verifyUrlMatch());

性能注意事项

  • 所有数据库异步操作必须加await控制流程,禁止无限制发起并发请求
  • 导入、校验前必须完成URL字段的索引创建,查询、写入速度可提升两个数量级
  • 批量操作单批数据量控制在1000-5000条,避免单次请求数据量过大触发MongoDB限制
  • 大数量查询使用lean()方法,跳过Mongoose文档实例化过程,可减少60%以上内存开销
  • 导入前提前对URL做去重、去空格处理,避免脏数据导致校验结果偏差

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 12:27:45