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)
关于您提到的 Node.js 和 MongoDB 的连接池问题,确实有些情况可能会导致您观察到的结果与预期不符。让我们先理解一下为什么连接池会有这样的效果,然后再提供一些优化建议。
为什么连接池设置较小反而更快?
-
并发限制:当
poolSize
设置为 1 时,意味着每次只能有一个请求进行数据库操作。这减少了并发操作的数量,因此在某些情况下,可能由于 I/O 等待时间减少,整体执行时间会缩短。 -
资源竞争:当
poolSize
较大时,多个并发操作同时访问数据库,可能会导致服务器端资源竞争(例如锁、内存等),从而增加延迟。 -
网络延迟:在某些情况下,网络延迟和数据库服务器的响应时间也会影响性能。
如何正确使用连接池?
为了更好地利用连接池的优势,可以考虑以下几点:
-
合理设置
poolSize
:根据您的应用负载调整poolSize
。通常,可以根据 CPU 核心数来设置一个合理的值。 -
使用最新的 MongoDB 驱动:确保您使用的是最新版本的 MongoDB 驱动程序,它可能包含性能优化。
-
批量操作:尽可能地使用批量操作(如批量插入)以减少数据库交互次数。
示例代码
下面是使用 mongodb
驱动程序的更新版代码,使用了 MongoClient
而不是原始的 Server
和 Db
类。
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时,虽然每次只能执行一个插入操作,但如果每次插入操作的延迟较小,总体上可能会比开启多个并发连接(即使总并发量一样)要快。这可能是由于每次插入后需要等待数据库响应,如果响应时间较长,频繁切换连接反而增加了额外的开销。
为了更好地利用连接池的优势,你可以尝试以下几点:
-
调整插入逻辑:确保在插入大量数据时能充分利用连接池的并发能力,而不是按顺序逐个插入。
-
增大poolSize:尝试更大的连接池大小,例如
poolSize: 100
或更大,观察是否能显著提升性能。 -
使用批量插入: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);
这段代码通过批量插入优化了性能,并且设置了适当的连接池大小以提高并发处理能力。