Nodejs mongodb 连接池问题

Nodejs mongodb 连接池问题

刚刚看了该论坛的该于mongodb连接池问题的帖子“mongodb驱动的正确使用方法” ,地址:http://cnodejs.org/topic/5190d61263e9f8a542acd83b 产生了疑惑!!! 当连接池poolSize设为10时,插入100万条数据,用时78472毫秒。当连接池poolSize设为1时,插入100万条数据,用时53454毫秒。 也就是说当连接数为1时,反而比连接数为10时用时更少!那要连接池还有什么用呢????

一下是测试代码,在Linux下运行 var server_options={‘auto_reconnect’:true,poolSize:1}; console.log(“poolSize:”+server_options.poolSize); var db_options={w:-1};

var mongodb = require(“mongodb”), mongoserver = new mongodb.Server(‘localhost’, 27017,server_options ), db = new mongodb.Db(‘test’, mongoserver, db_options);

db.open(function(err,db){ if(err)throw err; console.info(‘mongodb connected’); });

var time_start; function a(x){ db.collection(‘foo’).save({test:1},function(err,result){ if(x==1000000){ var now1=new Date(); var time_end=now1.getTime(); console.info(‘diff:’+(time_end-time_start)); }

});

} setTimeout(function(){ var now=new Date(); time_start=now.getTime();

for(var i=1;i<=1000000;i++) {

a(i);

} },2000)


5 回复

关于您提到的 Node.js 和 MongoDB 的连接池问题,确实有些情况可能会导致您观察到的结果与预期不符。让我们先理解一下为什么连接池会有这样的效果,然后再提供一些优化建议。

为什么连接池设置较小反而更快?

  1. 并发限制:当 poolSize 设置为 1 时,意味着每次只能有一个请求进行数据库操作。这减少了并发操作的数量,因此在某些情况下,可能由于 I/O 等待时间减少,整体执行时间会缩短。

  2. 资源竞争:当 poolSize 较大时,多个并发操作同时访问数据库,可能会导致服务器端资源竞争(例如锁、内存等),从而增加延迟。

  3. 网络延迟:在某些情况下,网络延迟和数据库服务器的响应时间也会影响性能。

如何正确使用连接池?

为了更好地利用连接池的优势,可以考虑以下几点:

  • 合理设置 poolSize:根据您的应用负载调整 poolSize。通常,可以根据 CPU 核心数来设置一个合理的值。

  • 使用最新的 MongoDB 驱动:确保您使用的是最新版本的 MongoDB 驱动程序,它可能包含性能优化。

  • 批量操作:尽可能地使用批量操作(如批量插入)以减少数据库交互次数。

示例代码

下面是使用 mongodb 驱动程序的更新版代码,使用了 MongoClient 而不是原始的 ServerDb 类。

const { MongoClient } = require('mongodb');

async function main() {
    const uri = 'mongodb://localhost:27017';
    const client = new MongoClient(uri, {
        useNewUrlParser: true,
        useUnifiedTopology: true,
        serverSelectionTimeoutMS: 5000,
        poolSize: 10 // 设置连接池大小
    });

    try {
        await client.connect();
        console.log("MongoDB connected");

        const db = client.db('test');
        const collection = db.collection('foo');

        const startTime = Date.now();

        for (let i = 1; i <= 1000000; i++) {
            await collection.insertOne({ test: 1 });
        }

        const endTime = Date.now();
        console.log(`Time taken: ${endTime - startTime} ms`);

    } catch (err) {
        console.error(err);
    } finally {
        await client.close();
    }
}

main().catch(console.error);

结论

连接池的主要目的是提高并发处理能力,但在特定场景下,过高的并发可能导致资源竞争和延迟增加。因此,需要根据具体应用场景调整 poolSize 并进行性能测试。上述代码展示了如何使用现代的 MongoDB 驱动程序并进行基本的性能测试。


mongodb 写的时候,同一个数据库,只能有一个进程可以使用当前入口。 你就是100个连接也没用。 因为并发已经被锁了。 这时连接数多了,反而增加CPU指令切换,他会“智能”的在几个写进程切换,让每一个都参与。

连接池的优势是读,这个不会被锁,而且即便并发写,在内存中也有记录,没有在写的数据依然可以同时读。

刚试了试,把db.collection(‘foo’).save({test:1},改成db.collection(‘foo’).find({test:1}。即写改成读操作,连续读取4千万条记录,发现连接数为10个和连接数为1个,时间上也没区别!!!甚至连接数为10个用时更长

能解释一下原因吗?读操作连接池也没有优势

连接池的主要作用是在高并发场景下提高数据库访问效率,避免每个请求都新建和销毁数据库连接,从而减少资源消耗和提高响应速度。但如果你的测试场景中数据写入操作的并行度较低,那么连接池的优势可能不明显,甚至在某些情况下(如网络延迟较大)会因为连接的创建与管理增加额外开销,导致性能不如单连接。

在你的测试中,poolSize设置为1时,虽然每次只能执行一个插入操作,但如果每次插入操作的延迟较小,总体上可能会比开启多个并发连接(即使总并发量一样)要快。这可能是由于每次插入后需要等待数据库响应,如果响应时间较长,频繁切换连接反而增加了额外的开销。

为了更好地利用连接池的优势,你可以尝试以下几点:

  1. 调整插入逻辑:确保在插入大量数据时能充分利用连接池的并发能力,而不是按顺序逐个插入。

  2. 增大poolSize:尝试更大的连接池大小,例如poolSize: 100或更大,观察是否能显著提升性能。

  3. 使用批量插入:MongoDB支持批量插入,可以一次插入多条数据,这样可以进一步减少数据库连接的开销。

以下是改进后的示例代码,演示了如何使用批量插入来提高效率:

const MongoClient = require('mongodb').MongoClient;

async function run() {
    const uri = "your_mongodb_connection_string";
    const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true, poolSize: 10 });

    try {
        await client.connect();
        console.log("Connected correctly to server");

        const collection = client.db("test").collection("foo");
        let items = [];

        for (let i = 0; i < 1000000; i++) {
            items.push({ test: 1 });
            if (items.length === 1000) { // 每1000条数据一起插入
                await collection.insertMany(items);
                items = []; // 清空数组准备下一批数据
            }
        }

        if (items.length > 0) { // 插入剩余的数据
            await collection.insertMany(items);
        }
    } catch (err) {
        console.error(err.stack);
    } finally {
        await client.close();
    }
}

run().catch(console.error);

这段代码通过批量插入优化了性能,并且设置了适当的连接池大小以提高并发处理能力。

回到顶部