Node.js:async.map越来越慢

时间:2016-06-07 16:18:22

标签: node.js mongodb async.js

您好,

我使用Node.js提供用于在MongoDB数据库上存储数据的API。

我在read方法上运行了多次测试,该方法接受id并返回相应的文档。关键是我必须按指定的顺序返回这些文档。为了确保这一点,我使用以下代码:

// 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();
}

这样,文档以正确的顺序逐个提取。我的笔记本电脑需要大约11秒来检索27k文件。

但是,我认为可以改进这种方法:

// 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。问题是一样的。什么导致这种延迟,我怎么能抑制它呢?

1 个答案:

答案 0 :(得分:4)

尝试使用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减少同步阻塞。