Node.js: async.map 越来越慢
Node.js: async.map getting slower
您好,
我使用 Node.js 提供一个 API 用于在 MongoDB 数据库上存储数据。
我 运行 对 read
方法进行了多次测试,该方法接受 ids 和 returns 相应的文档。关键是我必须 return 这些文件以指定的顺序排列。为确保这一点,我使用以下代码:
// Sequentially fetch every element
function read(ids, callback) {
var i = 0;
var results = [];
function next() {
db.findOne(ids[i], function (err, doc) {
results.push(err ? null : doc);
if (ids.length > ++i) {
return next();
}
callback(results);
});
}
next();
}
这样一来,文档就会按照正确的顺序一个接一个地获取。在我的笔记本电脑上检索 27k 个文档大约需要 11 秒。
但是,我认为可以改进此方法:
// Asynchronously map the whole array
var async = require('async');
function read(ids, callback) {
async.map(ids, db.findOne.bind(db), callback):
}
在 运行 一次测试后,我很满意地看到使用更简单的代码仅在 8 秒内检索了 27k 文档。
当我重复同一个请求时出现问题:响应时间不断增长(与检索到的元素数量成正比):9s 10s 11s 12s...
。顺序版不会出现这个问题
我尝试了两个版本的 Node.js,v6.2.0 和 v0.10.29。问题是一样的。是什么导致了这种延迟,我该如何抑制它?
尽量使用async.mapLimit
来防止过载。您需要一些测试来根据您的环境调整限值。
但 find({_id: {$in: list}})
总是更好,因为单个数据库请求而不是多个。
建议您尝试在客户端恢复原始订单。
像这样:
function read(ids, cb) {
db.find(
{_id: {$in: ids.map(id => mongoose.Types.ObjectId(id))}},
process
);
function process(err, docs) {
if (err) return cb(err);
return cb(null, docs.sort(ordering))
}
function ordering(a, b) {
return ids.indexOf(b._id.toString()) - ids.indexOf(a._id.toString());
}
}
可能是,查找查询需要更正,我无法知道您使用的确切 mongodb 驱动程序。
这段代码是第一次尝试,更多的手动排序可以提高性能。 [].indexOf
也很重(O(n)
)。
但我几乎肯定,即使现在这样,它也会工作得更快。
可能的 ordering
替换:
var idHash = {};
for(var i = 0; i < ids.length; i++)
idHash[ids[i]] = i;
function ordering(a, b) {
return idHash[b._id.toString()] - idHash[a._id.toString()];
}
任何排序算法在最好的情况下都有O(nlogn)
,但是我们已经知道每个找到的文档的结果位置,所以,我们可以通过O(n)
:
恢复原始顺序
var idHash = ids.reduce((c, id, i) => (c[id] = i, c), {});
function process(err, docs) {
if (err) return cb(err);
return cb(null,
docs.reduce(
(c, doc) => (c[idHash[doc._id.toString()]] = doc, c),
ids.map(id => null))) //fill not_found docs by null
}
函数式风格使代码更加灵活。例如,可以轻松修改此代码以使用 async.reduce
来减少同步阻塞。
您好,
我使用 Node.js 提供一个 API 用于在 MongoDB 数据库上存储数据。
我 运行 对 read
方法进行了多次测试,该方法接受 ids 和 returns 相应的文档。关键是我必须 return 这些文件以指定的顺序排列。为确保这一点,我使用以下代码:
// Sequentially fetch every element
function read(ids, callback) {
var i = 0;
var results = [];
function next() {
db.findOne(ids[i], function (err, doc) {
results.push(err ? null : doc);
if (ids.length > ++i) {
return next();
}
callback(results);
});
}
next();
}
这样一来,文档就会按照正确的顺序一个接一个地获取。在我的笔记本电脑上检索 27k 个文档大约需要 11 秒。
但是,我认为可以改进此方法:
// Asynchronously map the whole array
var async = require('async');
function read(ids, callback) {
async.map(ids, db.findOne.bind(db), callback):
}
在 运行 一次测试后,我很满意地看到使用更简单的代码仅在 8 秒内检索了 27k 文档。
当我重复同一个请求时出现问题:响应时间不断增长(与检索到的元素数量成正比):9s 10s 11s 12s...
。顺序版不会出现这个问题
我尝试了两个版本的 Node.js,v6.2.0 和 v0.10.29。问题是一样的。是什么导致了这种延迟,我该如何抑制它?
尽量使用async.mapLimit
来防止过载。您需要一些测试来根据您的环境调整限值。
但 find({_id: {$in: list}})
总是更好,因为单个数据库请求而不是多个。
建议您尝试在客户端恢复原始订单。
像这样:
function read(ids, cb) {
db.find(
{_id: {$in: ids.map(id => mongoose.Types.ObjectId(id))}},
process
);
function process(err, docs) {
if (err) return cb(err);
return cb(null, docs.sort(ordering))
}
function ordering(a, b) {
return ids.indexOf(b._id.toString()) - ids.indexOf(a._id.toString());
}
}
可能是,查找查询需要更正,我无法知道您使用的确切 mongodb 驱动程序。
这段代码是第一次尝试,更多的手动排序可以提高性能。 [].indexOf
也很重(O(n)
)。
但我几乎肯定,即使现在这样,它也会工作得更快。
可能的 ordering
替换:
var idHash = {};
for(var i = 0; i < ids.length; i++)
idHash[ids[i]] = i;
function ordering(a, b) {
return idHash[b._id.toString()] - idHash[a._id.toString()];
}
任何排序算法在最好的情况下都有O(nlogn)
,但是我们已经知道每个找到的文档的结果位置,所以,我们可以通过O(n)
:
var idHash = ids.reduce((c, id, i) => (c[id] = i, c), {});
function process(err, docs) {
if (err) return cb(err);
return cb(null,
docs.reduce(
(c, doc) => (c[idHash[doc._id.toString()]] = doc, c),
ids.map(id => null))) //fill not_found docs by null
}
函数式风格使代码更加灵活。例如,可以轻松修改此代码以使用 async.reduce
来减少同步阻塞。