我正在使用风帆0.11.2。使用最新的sails-mongo适配器。 我有一个非常大的数据库(千兆字节的数据),主要是时间戳和值。我使用蓝图api对它进行查询。
如果我使用localhost:1337 / datatable?limit = 100000000000进行查询,则nodejs在0.12上挂起,占用大量CPU,并在v4上崩溃。它在toJSON函数上崩溃。
我发现我需要对我的API进行多次查询。但我不知道怎么做。
如何进行多次查询“不爆炸”我的服务器?
更新
在带有最新水线和sails-mongo的更新版本0.12.3上,查询更加顺畅。云上的崩溃是因为我没有足够的RAM来处理同一个T2.micro实例上的sailsjs和mongodb。
我已将mongodb服务器移动到M3.Medium实例。现在服务器不再崩溃,但它冻结了。我正在使用跳过限制,它适用于sails.js,但对于mongodb来说是一个很大的资源浪费!
Mongodb使用limit = skip + limit进行内部查询。然后将光标移动到所需的数据并返回。当你在分页中做很多事情时,你会使用大量的内部查询。随着查询大小的增加。
答案 0 :(得分:1)
正如this article所解释的那样,避免使用MongoDB中浪费资源的方法是避免使用skip
并巧妙地使用_id
作为查询的一部分。
我没有使用sails mongo,但我确实通过在nodejs中使用mongo驱动程序来实现上述想法:
/**
* Motivation:
* Wanted to put together some code that used:
* - BlueBird (promises)
* - MongoDB NodeJS Driver
* - and paging that did not rely on skip()
*
* References:
* Based on articles such as:
* https://scalegrid.io/blog/fast-paging-with-mongodb/
* and GitHub puclic code searches such as:
* https://github.com/search?utf8=%E2%9C%93&q=bluebird+MongoClient+_id+find+limit+gt+language%3Ajavascript+&type=Code&ref=searchresults
* which yielded smaple code hits such as:
* https://github.com/HabitRPG/habitrpg/blob/28f2e9c356d7053884107d90d04e28dde75fa81b/migrations/api_v3/coupons.js#L71
*/
var Promise = require('bluebird'); // jshint ignore:line
var _ = require('lodash');
var MongoClient = require('mongodb').MongoClient;
var dbHandleForShutDowns;
// option a: great for debugging
var logger = require('tracer').console();
// option b: general purpose use
//var logger = console;
//...
var getPage = function getPage(db, collectionName, query, projection, pageSize, processPage) {
//console.log('DEBUG', 'filter:', JSON.stringify(query,null,2));
projection = (projection) ? projection['_id']=true : {'_id':true};
return db
.collection(collectionName)
.find(query)
.project(projection)
.sort({'_id':1}).limit(pageSize)
.toArray() // cursor methods return promises: http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html#toArray
.then(function processPagedResults(documents) {
if (!documents || documents.length < 1) {
// stop - no data left to traverse
return Promise.resolve();
}
else {
if (documents.length < pageSize) {
// stop - last page
return processPage(documents);
}
else {
return processPage(documents) // process the results of the current page
.then(function getNextPage(){ // then go get the next page
var last_id = documents[documents.length-1]['_id'];
query['_id'] = {'$gt' : last_id};
return getPage(db, collectionName, query, projection, pageSize, processPage);
});
}
}
});
};
//...
return MongoClient
.connect(params.dbUrl, {
promiseLibrary: Promise
})
.then(function(db) {
dbHandleForShutDowns = db;
return getPage(db, collectionName, {}, {}, 5, function processPage(pagedDocs){console.log('do something with', pagedDocs);})
.finally(db.close.bind(db));
})
.catch(function(err) {
console.error("ERROR", err);
dbHandleForShutDowns.close();
});
以下两节介绍了代码如何操作_id
并使其成为查询的一部分:
.sort({'_id':1}).limit(pageSize)
// [...]
var last_id = documents[documents.length-1]['_id'];
query['_id'] = {'$gt' : last_id};
整体代码流程:
让getPage()
处理工作,您可以根据自己的喜好设置pageSize
和query
:
return getPage(db, collectionName, {}, {}, 5, function processPage(pagedDocs){console.log('do something with', pagedDocs);})
方法签名:
var getPage = function getPage(db, collectionName, query, projection, pageSize, processPage) {
一旦可用,请处理pagedResults
:
return processPage(documents) // process the results of the current page
转到下一页:
return getPage(db, collectionName, query, projection, pageSize, processPage);
当没有剩余数据时,代码将停止:
// stop - no data left to traverse
return Promise.resolve();
或者在处理最后一页数据时它会停止:
// stop - last page
return processPage(documents);
我希望这会提供一些灵感,即使它不是满足您需求的精确解决方案。
答案 1 :(得分:1)
1。运行汇总
const SailsMongoQuery = require('sails-mongo/lib/query/index.js')
const SailsMongoMatchMongoId = require('sails-mongo/lib/utils.js').matchMongoId
const fn = model.find(query).paginate(paginate)
const criteria = fn._criteria
const queryLib = new SailsMongoQuery(criteria, {})
const queryOptions = _.omit(queryLib.criteria, 'where')
const where = queryLib.criteria.where || {}
const queryWhere = Object.keys(where).reduce((acc, key) => {
const val = where[key]
acc[key] = SailsMongoMatchMongoId(val) ? new ObjectID(val) : val
return acc
}, {})
const aggregate = [
{ $match: queryWhere }
].concat(Object.keys(queryOptions).map(key => ({ [`$${key}`]: queryOptions[key] })))
// console.log('roge aggregate --->', JSON.stringify(aggregate, null, 2))
model.native((err, collection) => {
if (err) return callback(err)
collection.aggregate(aggregate, { allowDiskUse: true }).toArray(function (err, docs) {
if (err) return callback(err)
const pk = primaryKey === 'id' ? '_id' : primaryKey
ids = docs.reduce((acc, doc) => [...acc, doc[pk]], [])
callback()
})
})
2。按ID查找航行
query = Object.assign({}, query, { [primaryKey]: ids }) // check primary key in sails model
fn = model.find(query) // .populate or another method
fn.exec((err, results) => { console.log('result ->>>>', err, results) })