2017-04-10 119 views
0

我有一個API端點,我試圖強調測試,它讀取非常大的數據庫集合(200萬個文檔)MongoDB。每個查詢需要大約2秒然而,我遇到的問題是到數據庫的連接沒有正確合併,因此每個查詢順序運行而不是併發MongoDB按順序執行查詢而不是並行執行查詢

我正在使用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的調用。現在,這裏的問題出在哪裏,這個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的實例。

+0

在MongoDB方面,您是否有查詢搜索條件的索引?你對數據庫的響應時間應該小得多,看看[這裏](https://docs.mongodb.com/manual/indexes/「here」)。 –

+0

是的,我有一個地理空間索引,但在這種情況下,它不會產生任何影響,因爲我通過未編入索引的郵政編碼進行搜索。請參閱我的問題上的編輯。 –

+0

我在Postcode字段中添加了一個索引,現在它的速度非常快,謝謝你的提示。 +1。 –

回答

0

好吧,所以我設法弄清楚問題是什麼。

首先,發出查詢時(見here),MongoDB有一個讀鎖。這就是它按順序執行查詢的原因。進一步改進的唯一方法是通過分片收集。

另外,如Jorge建議的,我在郵編字段中添加了一個索引,這大大減少了延遲。

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毫秒這是一個驚人的進步。

1

首先,MongoDB在發出查詢時有一個讀鎖(請參見這裏)。這就是它按順序執行查詢的原因。進一步改進的唯一方法是分解集合。

如果您使用mongo 3.0+ with wiredtiger作爲存儲引擎,則您具有文檔級鎖定功能。查詢不應該順序執行,分片肯定會對平行主義有所幫助,但對於大多數現代計算機/服務器硬件而言,2kk文檔不應該成爲問題。

你在第一個問題中提到了mongodb的日誌文件,你應該打開多個連接,是這種情況嗎?

+0

對不起,發佈另一個答案,而不是評論,我還沒有代表尚未這樣做,:/ –

+0

是的,它似乎通過使用池本身的行爲。但是,即使池在這種情況下也不會因爲集合上的鎖定而有所作爲,所以即使10個池用戶試圖運行查詢,也只會一次執行一次。我會研究有線和分片,謝謝你的幫助。 +1 –