提高Mongo查询性能

3
我正在使用一个名为Keystone的基于Node的CMS系统,它使用MongoDB作为数据存储,因此对数据和访问具有相当自由的控制权。我有一个非常复杂的模型叫做“Family”,它有大约250个字段、一堆关系和十几个方法。我的网站上有一个表单,允许用户输入必要的信息来创建一个新的“Family”记录,但是处理时间很长(本地主机上为12秒,在我的Heroku实例上超过30秒)。我遇到的问题是Heroku会对运行超过30秒的任何进程发出应用程序错误,这意味着我需要优化我的查询。所有处理都非常快,除了一个函数。以下是造成问题的函数:
const Family = keystone.list( 'Family' );

exports.getNextRegistrationNumber = ( req, res, done ) => {

    console.time('get registration number');

    const locals = res.locals;

    Family.model.find()
        .select( 'registrationNumber' )
        .exec()
        .then( families => {

            // get an array of registration numbers
            const registrationNumbers = families.map( family => family.get( 'registrationNumber' ) );

            // get the largest registration number
            locals.newRegistrationNumber = Math.max( ...registrationNumbers ) + 1;

            console.timeEnd('get registration number');

            done();

        }, err => {

            console.timeEnd('get registration number');

            console.log( 'error setting registration number' );

            console.log( err );

            done();
        });

};

我的.then()中的处理只需几毫秒,但是Family.model.find()执行起来太慢了。有什么建议可以加快速度将不胜感激。查询正在尝试浏览大约40,000个家庭记录,并且registrationNumber字段上已经存在索引。

1个回答

6
很明显,then() 执行速度很快,但 find() 需要一段时间;在一组记录中找到最大值是一个相对较快的数据库操作,而获取该组数据可能会因多种因素而非常耗时。如果您只是读取数据并通过 REST 或某种可视界面呈现给用户,可以使用 lean() 返回纯 JavaScript 对象。默认情况下,您返回的是一个 mongoose.Document,在您的情况下它是不必要的,因为在读取查询后似乎没有进行任何数据操作;你只是获取数据。
更重要的是,看起来你只需要一个记录:具有最大 registrationNumber 的记录。当您在任何一组记录中寻找一个记录以最大化性能时,应始终使用 findOne()
请参阅 前面的答案 详细介绍了在 node.js 实现中使用 findOne,或者请参阅 mongoDB 文档 以获得有关此集合方法的一般信息。

是的,查找存储值的最大值是数据库擅长的事情。 - Sergio Tulentsev
感谢 @lax1089 的帮助。看起来有很多因素,其中之一是 lean()。我没有意识到Mongo在获取数据方面是多么高效,问题是查询后发生的所有事情都导致了减速。我学到的教训是即使查询本身很复杂,返回的对象大小也很重要。我添加了 .lean(),但也利用了 .where() 在查询中处理结果集的缩减,而不是在接收结果后进行逻辑处理,还使用了 .select() 选择所需字段。 - autoboxer
1
虽然它是特定于Node的,但值得注意的是,进行性能测试和调整的简单方法是在要测试的内容周围添加console.time('thing to time')和console.timeEnd('thing to time')。 它将输出执行该操作所需的毫秒数。 我习惯在新代码中创建每个异步函数的开头和结尾放置它,以查看是否存在任何长时间运行的块。 如果我发现有任何问题,我会更改我的测试范围,以确定减速发生的确切位置。 - autoboxer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接