我只是在使用以下代码写入MongoDB:
let MongoClient = require('mongodb').MongoClient
MongoClient.connect(mongoUrl,function(error,db){
if (error) throw new Error(error);
var dbo = db.db(mongoDatabase);
dbo.collection(myCollection).insertOne(item, function(err, res) {
if (err) throw err;
db.close()
})
})
如果我仅对一个插入执行上述代码,则一切正常。 但是,如果我要执行许多插入操作,例如,如果我在循环语句中调用上面的代码,则真正的写操作会在循环结束时一次全部发生。
例如,如果我在100个循环的循环中调用上述代码,则第100个循环之后的所有相同时间都会发生100次插入。最大的问题是,发生插入操作时,会同时打开许多与MongoDB的连接,并且如果我超过了打开的最大文件数操作系统限制,我还会遇到“打开的文件太多”错误。
我知道我可以使用bulkWrite和insertMany操作,但是我只想知道是否有一种方法可以在每个insertOne之后而不是循环结束时在数据库中进行真正的写入。
似乎MongoClient管理着某种队列,等待对同一集合进行类似的操作,在这种情况下,它会收集所有这些操作以一次完成所有操作。我只想避免这种情况,并使每个操作实时发生。
更新-2018年10月10日
如果我使用以下代码调用上面发布的代码
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
drawModel.saveDraw({test: "1"})
sleep(2000)
drawModel.saveDraw({test: "1"})
sleep(2000)
drawModel.saveDraw({test: "1"})
sleep(2000)
drawModel.saveDraw({test: "1"})
sleep(2000)
drawModel.saveDraw({test: "1"})
sleep(2000)
在执行代码期间,我没有看到数据库中的任何更改,并且在mongo控制台中,我也没有看到任何写入。我插入2秒钟的睡眠,以便有时间在执行代码时进行验证。 在最后一次睡眠结束时,所有写入操作均同时发生。
2018-10-10T15:44:25.649+0200 I NETWORK [initandlisten] connection accepted from 127.0.0.1:54855 #5 (5 connections now open)
2018-10-10T15:44:25.649+0200 I NETWORK [initandlisten] connection accepted from 127.0.0.1:54856 #6 (6 connections now open)
2018-10-10T15:44:25.649+0200 I NETWORK [initandlisten] connection accepted from 127.0.0.1:54857 #7 (7 connections now open)
2018-10-10T15:44:25.649+0200 I NETWORK [initandlisten] connection accepted from 127.0.0.1:54858 #8 (8 connections now open)
2018-10-10T15:44:25.649+0200 I NETWORK [initandlisten] connection accepted from 127.0.0.1:54859 #9 (9 connections now open)
2018-10-10T15:44:25.658+0200 I NETWORK [conn5] end connection 127.0.0.1:54855 (8 connections now open)
2018-10-10T15:44:25.658+0200 I NETWORK [conn6] end connection 127.0.0.1:54856 (7 connections now open)
2018-10-10T15:44:25.658+0200 I NETWORK [conn7] end connection 127.0.0.1:54857 (6 connections now open)
2018-10-10T15:44:25.658+0200 I NETWORK [conn8] end connection 127.0.0.1:54858 (5 connections now open)
2018-10-10T15:44:25.659+0200 I NETWORK [conn9] end connection 127.0.0.1:54859 (4 connections now open)
如您所见,并发写入会导致并发连接和随之而来的并发文件打开。我想避免这种情况,并使写入操作实时发生。
答案 0 :(得分:0)
这通常是关于节点编程的更一般的事情。一切都在单个线程中运行,回调在循环后运行,这是使用回调API的;要逐个序列化操作,您需要将下一个操作放入上一个操作的回调中。您不能使用for或while之类的循环。您必须编写一个递归函数来序列化操作。批处理功能是为您简化事情的地方
如果将诺言样式API与较新版本的节点一起使用,您将能够使用async await语法并编写看起来像带有循环的同步代码
编辑:
当我说过无法通过循环执行此操作时,我并不是说要手动写出循环操作。我的意思是为了确保您的操作一次完成(序列化),您需要将下一个操作放入上一个操作的回调中。例如,您有三个异步执行功能的函数-使用回调API,它们采用一个回调参数,这是约定的最后一个参数。为了确保这三件事依次发生,您需要编写如下代码:
function doThings (callback) {
firstThing(argsForFirstThing, function (error, resultOfFirstThing) {
if (error) console.log(err)
else secondThing(argsForSecondThing, function (error, resultOfSecondThing) {
if (error) console.log(err)
else thirdThing(argsForThirdThing, callback)
}
}
}
要依次执行不确定的事情,您将需要编写一个递归函数或查看npm的async
包
EDIT2:
关于您发布的代码; NodeJS是单线程事件分配器。一批代码将运行完毕,并且回调仅在此之后发生。不要那样阻塞代码,因为在您放弃控制之前,回调显然不会执行
如果您确实要等待,请使用setTimeout
,如下所示:
// .. do stuff
setTimeout(function () {
console.log('the rest of your code here after 2 seconds')
}, 2000)
在您的示例中,假设saveDraw
看起来像这样:
saveDraw(options, callback) {
MongoClient.connect(mongoUrl,function(error,db){
if (error) throw new Error(error);
var dbo = db.db(mongoDatabase);
dbo.collection(myCollection).insertOne(item, function(err, res) {
if (err) throw err;
db.close(callback)
})
})
请注意传递给close的回调参数。然后您的调用代码应如下所示:
saveDraw({test: '1'}, (err) => {
saveDraw({test: '2'}, (err) => {
saveDraw({test: '3'} (err) => {
console.log('I saved 3 things')
})
})
})