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

MongoDB并行插入100条记录时的性能差异问题

MongoDB并行插入性能下降的原因及解决办法

性能差异的原因

  • 连接池耗尽:MongoDB Node.js驱动默认连接池大小为100,当一次性发起100个insertOne请求时,连接池被完全占满。服务器端需要同时处理所有连接的请求,每个请求的等待和处理时间被直接拉长。
  • 服务器资源竞争:Atlas M30属于共享型集群,CPU、磁盘IO和网络带宽都有上限。100个并发插入请求会引发服务器端上下文切换、文档锁协调开销(虽为文档级锁,但批量并发仍有资源竞争),以及网络带宽饱和,直接导致单请求响应时间飙升。
  • 客户端并发压力:一次性触发100个异步Promise,客户端事件循环需要处理大量网络回调,加上网络往返的排队延迟,进一步增加了单条插入的耗时。

解决办法

1. 控制并发请求数

避免一次性发起全部请求,采用分批并发的方式,限制同时进行的插入请求数量,让服务器和连接池有足够资源处理请求。可以使用p-limit库简化控制:

import { MongoClient } from 'mongodb';
import pLimit from 'p-limit';

const mc = new MongoClient("mongodb://localhost:27017/");
// 限制并发数为20,可根据实际情况调整
const limit = pLimit(20);

const getConnection = async function () {
    const client = await mc.connect();
    const db = client.db('testpeformance');
    return db.collection("candidate");
};

const insertToMongo = async function (collection, index) {
    const startTime = new Date();
    const result = await collection.insertOne({
        "FirstName": "TESTPERF",
        "LastName": "TESTPERF",
        "Designation": "TESTPERF",
        "EmailId": "dhanilan@undaku.com"
    });
    const endTime = new Date();
    const insertedId = result.insertedId;
    console.log(`Inserting record [${index}] - ${insertedId} took ${endTime.getTime() - startTime.getTime()}ms`);
};

const run = async function () {
    const connection = await getConnection();
    console.time('overall');
    const promises = [];
    for (let index = 0; index < 100; index++) {
        promises.push(limit(() => insertToMongo(connection, index)));
    }
    await Promise.all(promises);
    console.timeEnd('overall');
};
run();

如果不想额外引入库,也可以手动实现分批处理:

const run = async function () {
    const connection = await getConnection();
    console.time('overall');
    const batchSize = 20; // 每批并发数
    for (let topIndex = 0; topIndex < 100; topIndex += batchSize) {
        const promises = [];
        for (let index = topIndex; index < Math.min(topIndex + batchSize, 100); index++) {
            promises.push(insertToMongo(connection, index));
        }
        await Promise.all(promises);
    }
    console.timeEnd('overall');
};

2. 调整连接池大小

根据服务器承载能力,适当调整MongoDB客户端的连接池大小,避免连接池成为瓶颈。初始化MongoClient时配置maxPoolSize:

const mc = new MongoClient("mongodb://localhost:27017/", {
    maxPoolSize: 150 // 不要超过Atlas M30的连接数上限(约1000/节点)
});

3. 优化预处理与插入的并行逻辑

虽然无法使用批量插入,但可以将预处理和插入操作重叠执行:比如预处理第1-20条文档的同时,发起前一批(0-19)的插入请求,减少整体等待时间。

4. 临时调整索引

如果集合上存在非必要索引,大量插入时会产生索引更新的额外开销。可以在插入前临时删除非必需索引,插入完成后再重建:

// 插入前删除索引
await connection.dropIndex("EmailId_1");
// 执行插入操作...
// 插入完成后重建索引
await connection.createIndex({ EmailId: 1 });

5. 检查Atlas实例负载

登录Atlas控制台查看M30实例的CPU、磁盘IO、网络使用率,如果已接近上限,可考虑升级实例规格(如M40),或调整实例配置(如增加节点)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 01:45:44