【问题标题】:MongoDB executes queries sequentially instead of in parallelMongoDB 按顺序而不是并行执行查询
【发布时间】:2017-09-05 21:32:53
【问题描述】:

我有一个 API 端点,我正在尝试对其进行压力测试,它读取一个非常大的 MongoDB 数据库集合(200 万个文档)。每个查询大约需要 2 秒,但是我遇到的问题是与数据库的连接没有被正确池化,因此每个查询 sequentially 而不是 concurrently 运行

我使用Mongoose 连接到我的数据库,我使用artillery.io 进行测试。

这是我的连接代码:

const mongoose = require('mongoose');
const Promise = require('bluebird');

const connectionString = process.env.MONGO_DB || 'mongodb://localhost/mydatabase';

mongoose.Promise = Promise;

mongoose.connect(connectionString, {
    server: { poolSize: 10 }
});

const db = mongoose.connection;

db.on('error', console.error.bind(console, 'connection error: '));

db.once('open', function() {
    console.log('Connected to: ' + connectionString);
});

module.exports = db;

这是您的标准连接程序,但最重要的部分可能是server: { poolSize: 10 } 行。

我正在使用以下脚本进行 artillery.io 测试:

config:
  target: 'http://localhost:1337'
  phases:
    -
      duration: 10
      arrivalRate: 5
      name: "Warm-up"

scenarios:
  -
    name: "Search by postcodes"
    flow:
      -
        post:
          url: "/api/postcodes/gb_full/search"
          headers:
            Content-Type: 'application/json'
          json:
            postcodes:
              - ABC 123,
              - DEF 345,
              - GHI 678

此测试在 10 秒内对 API 执行 50 次调用。现在问题出在此处,API 似乎按顺序执行查询,请参阅下面的测试结果:

"latency": {
  "min": 1394.1,
  "max": 57693,
  "median": 30222.7,
  "p95": 55396.8,
  "p99": 57693
},

并且数据库日志如下:

connection accepted from 127.0.0.1:60770 #1 (1 connection now open)
...
2017-04-10T18:45:55.389+0100 ... 1329ms
2017-04-10T18:45:56.711+0100 ... 1321ms
2017-04-10T18:45:58.016+0100 ... 1304ms
2017-04-10T18:45:59.355+0100 ... 1338ms
2017-04-10T18:46:00.651+0100 ... 1295ms

API 似乎只使用一个连接,这似乎是正确的,但据我了解,这将自动充分利用 poolSize并发执行这些查询而不是一个一次。

我在这里做错了什么?如何并行执行这些数据库查询?


编辑 1 - 模型和查询

希望让事情更清楚一点,我使用以下模型:

const mongoose = require('mongoose');
const db = require('...');

const postcodeSchema = mongoose.Schema({
    postcode: { type: String, required: true },
    ...
    location: {
      type: { type: String, required: true },
      coordinates: [] //coordinates must be in longitude, latitude order.
    }
});

//Define the index for the location object.
postcodeSchema.index({location: '2dsphere'});

//Export a function that will allow us to define the collection
//name so we'll pass in something like: GB, IT, DE ect for different data sets.
module.exports = function(collectionName) {
    return db.model('Postcode', postcodeSchema, collectionName.toLowerCase());
};

db 对象是本问题顶部解释的连接模块。

我正在使用以下命令执行查询:

/**
 * Searches and returns GeoJSON data for a given array of postcodes.
 * @param {Array} postcodes - The postcode array to search.
 * @param {String} collection - The name of the collection to search, i.e 'GB'.
 */
function search(postcodes, collection) {
    return new Promise((resolve, reject) => {
        let col = new PostcodeCollection(collection.toLowerCase());

        col.find({
            postcode: { $in: postcodes }
        })
        .exec((err, docs) => {
            if (err)
                return reject(err);

            resolve(docs);
        });
    });
}

下面是如何调用函数的示例:

search(['ABC 123', 'DEF 456', 'GHI 789'], 'gb_full')
.then(postcodes => {
    console.log(postcodes);
})
.catch(...);

为了重新迭代,这些查询是通过node.js API 执行的,因此它们应该已经是异步的,但是查询本身是一个接一个地执行的。因此,我相信问题可能出在 MongoDB 方面,但我什至不知道从哪里开始寻找。如果已经有一个正在运行,就好像 MongoDB 正在阻止对集合执行任何其他查询。

我在 Windows 10 机器上本地运行 mongod.exe 的实例。

【问题讨论】:

  • 在 MongoDB 方面,您的查询搜索条件是否有索引?你对数据库的响应时间应该会小很多,看看here
  • 是的,我有一个地理空间索引,但是在这种情况下它不会有任何效果,因为我正在按未编入索引的邮政编码进行搜索。请查看我的问题的编辑。
  • 我在 Postcode 字段中添加了一个索引,现在速度非常快,感谢您的提示。 +1。

标签: javascript node.js mongodb mongoose stress-testing


【解决方案1】:

首先,MongoDB 在发出查询时有一个读锁(见这里)。这就是它按顺序执行查询的原因。进一步改进这一点的唯一方法是对集合进行分片。

如果您使用带有wiredtiger 的mongo 3.0+ 作为存储引擎,则您有文档级锁定。查询不应该按顺序执行,分片肯定有助于并行性,但 2kk 文档对于大多数现代计算机/服务器硬件来说应该不是问题。

你在第一个问题提到了mongodb的日志文件,你应该打开了多个连接,是这样吗?

【讨论】:

  • 抱歉发布另一个答案,而不是评论,我还没有代表这样做,:/
  • 是的,它似乎通过使用池来表现自己。然而,由于集合上的锁定,即使池化在这种情况下也不会产生影响,因此即使 10 个池用户尝试运行查询,它一次也只会执行一个。我会研究wiredtiger和sharding,谢谢你的帮助。 +1
【解决方案2】:

好的,所以我设法找出问题所在。

首先,MongoDB 在发出查询时有一个读锁(请参阅here)。这就是它按顺序执行查询的原因。进一步改进这一点的唯一方法是对集合进行分片

另外,正如 Jorge 所建议的,我在 postcode 字段上添加了一个索引,这大大减少了延迟。

postcodeSchema.index({ postcode: 1 }); //, { unique: true } is a tiny bit faster.

换个角度来看,以下是新指标到位后的压力测试结果:

"latency": {
  "min": 5.2,
  "max": 72.2,
  "median": 11.1,
  "p95": 17,
  "p99": null
},

中位延迟已从 30 秒 降至 11 毫秒,这是一个惊人的进步。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    • 2017-03-17
    • 2014-03-05
    • 1970-01-01
    • 2015-06-15
    相关资源
    最近更新 更多