【发布时间】:2018-01-13 14:43:17
【问题描述】:
我正在我的服务器上尝试 XML -> JSON -> MongoDB。我有一个 NodeJS 应用程序,它流式传输 XML,将其转换为 JSON,然后以 1000 块为单位将其添加到 MongoDB 服务器。然而,在大约 75000 条记录之后,我的 Macbook 的风扇开始转得更快,处理过程变得非常缓慢。几分钟后,我收到此错误:
[30517:0x102801600] 698057 毫秒:标记扫描 1408.2 (1702.9) -> 1408.1 (1667.4) MB,800.3 / 0.0 毫秒(+ 0.0 毫秒,自标记开始以来的 0 步,最大步长 0.0 毫秒,自开始以来的墙时间标记 803 毫秒)最后的手段 [30517:0x102801600] 698940 ms:Mark-sweep 1408.1 (1667.4) -> 1408.1 (1667.4) MB,882.2 / 0.0 ms 最后一招
最后在 JS 堆栈跟踪中:
致命错误:CALL_AND_RETRY_LAST 分配失败 - JavaScript 堆内存不足
我感觉我的内存快用完了,但是当文件超过 70 GB 并且我只有 16 GB 的 RAM 时,使用 --max-old-space-size(或其他)增加允许的内存不起作用。
这是我正在尝试做的代码:
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();
}
});
});
我的问题类似于:我有内存泄漏吗?为什么 Node 允许代码像那样构建内存?除了为我的 PC 购买 70+ GB 的 RAM 之外,还有其他解决方法吗?
【问题讨论】:
-
很有可能
1000的批处理大小实际上太大而无法将内容存储在数组中,但不太可能,因为 BSON 限制为 16MB,如果提供数组内容,insertMany()将简单地失败超过了那个尺寸。如果有任何“泄漏”,则必须在 XML 流本身中。 -
我要告诉你的是,由于 BSON 限制,它可能不会有任何区别。 “流”意味着只处理匹配的每个项目。这实际上会在每个异步调用上“暂停”和“恢复”,因此应该只有活动的批处理集合,没有其他正在处理的项目。但是,如果 XML 流中存在错误,则它不会从先前解析的项目中释放内存。与数据库或操作无关。
-
我认为这是您对
.collect的使用我认为它只需要收集子项目并且您可能不需要调用它,因为我可以看到您已经在处理的名称反正元素。很难说没有看到你的结构。即使您正在流式传输它们,collect也会强制将它们保存在内存中。 -
刚刚浏览了 xml-stream 文档和示例,
collect的使用在这里似乎不正确。看起来您的实际事件处理应该是xml.on("updateElement: ns:Statistik", ...或者可能只是endElement而根本不使用collect。它的预期目的是“收集子节点”,而您想要的是收集特定节点匹配的所有数据。见github.com/assistunion/xml-stream/blob/master/examples/…。我的猜测是“收集”是“坚持”你真正想丢弃的物品。 -
感谢@Strelok。我只是查看了文档并得出了结论,当我在这里查看时,他们刚刚发布了相同的结论。我只是添加指出使用示例。
标签: javascript node.js mongodb memory