我试图有效地插入大量数据(XML文件大小超过70GB)而不会崩溃我的MongoDB服务器。目前,这就是我在NodeJS中使用xml-stream
所做的事情:
xml.on()
当我调用db.collection().insertOne()
时,它基本上会返回我当前所在的树/元素。由于这是直接的JSON,我可以将它作为参数提供给我的insertMany()
函数,它会将它完全插入到我想要的数据库中。
所有代码实际上都可以正常工作,但在大约3000次插入(大约需要10秒)后停止。我怀疑它是因为我打开数据库连接,插入数据,然后每次在XML文件中看到一棵树时关闭连接,在这种情况下大约3000次。
我可以以某种方式合并theano
函数并以100s(或更多)的块来执行,但我不太确定如何将它与流式和异步一起使用。< / p>
所以我的问题是:如何在没有崩溃的情况下将大量XML(到JSON)插入我的MongoDB数据库?
答案 0 :(得分:3)
你认为.insertMany()
比每次写作更好是正确的,所以这只是收集"stream"上的数据的问题。
由于执行是“异步”,您通常希望避免在堆栈上有太多活动调用,因此通常在调用.pause()
之前"stream" .insertMany()
然后{ {3}}回调完成后:
var fs = require('fs'),
path = require('path'),
XmlStream = require('xml-stream'),
MongoClient = require('mongodb').MongoClient,
url = 'mongodb://username:password@my.server:27017/mydatabase',
amount = 0;
MongoClient.connect(url, function(err, db) {
var stream = fs.createReadStream(path.join(__dirname, 'motor.xml'));
var xml = new XmlStream(stream);
var docs = [];
//xml.collect('ns:Statistik');
// This is your event for the element matches
xml.on('endElement: ns:Statistik', function(item) {
docs.push(item); // collect to array for insertMany
amount++;
if ( amount % 1000 === 0 ) {
xml.pause(); // pause the stream events
db.collection('vehicles').insertMany(docs, function(err, result) {
if (err) throw err;
docs = []; // clear the array
xml.resume(); // resume the stream events
});
}
});
// End stream handler - insert remaining and close connection
xml.on("end",function() {
if ( amount % 1000 !== 0 ) {
db.collection('vehicles').insertMany(docs, function(err, result) {
if (err) throw err;
db.close();
});
} else {
db.close();
}
});
});
或者甚至对它进行现代化改造:
const fs = require('fs'),
path = require('path'),
XmlStream = require('xml-stream'),
MongoClient = require('mongodb').MongoClient;
const uri = 'mongodb://username:password@my.server:27017/mydatabase';
(async function() {
let amount = 0,
docs = [],
db;
try {
db = await MongoClient.connect(uri);
const stream = fs.createReadStream(path.join(__dirname, 'motor.xml')),
xml = new XmlStream(stream);
await Promise((resolve,reject) => {
xml.on('endElement: ns:Statistik', async (item) => {
docs.push(item);
amount++;
if ( amount % 1000 === 0 ) {
try {
xml.pause();
await db.collection('vehicle').insertMany(docs);
docs = [];
xml.resume();
} catch(e) {
reject(e)
}
}
});
xml.on('end',resolve);
xml.on('error',reject);
});
if ( amount % 1000 !== 0 ) {
await db.collection('vehicle').insertMany(docs);
}
} catch(e) {
console.error(e);
} finally {
db.close();
}
})();
请注意,MongoClient
连接实际上包含了所有其他操作。您只想连接一次,其他操作发生在.resume()
的事件处理程序上。
因此,对于XMLStream
,在表达式匹配时触发事件处理程序,并将数据提取并收集到数组中。 "stream"调用的每1000个项目用于插入文档,“暂停”和“恢复”“异步”调用。
完成后,.insertMany()
会触发“结束”事件。这是关闭数据库连接的地方,事件循环将被释放并结束程序。
虽然可以通过允许一次发出各种"stream"调用来获得某种程度的“并行性”(并且通常为“汇集大小”以便不会超出调用堆栈),这基本上是如何通过简单地暂停等待其他异步I / O完成,该过程以最简单的形式查找。
注意:根据
.insertMany()
注释原始代码中的.collect()
方法,这似乎没有必要,实际上是将节点保留在内存中每次写入数据库后都应该丢弃。