我对性能问题有些担心。我有一个cron,它每分钟运行一次,并更新一个集合。
每分钟从外部api获取10,000个新交易。其中一些交易记录已经在我的数据库中。
for (transaction in transactions) {
if (Transaction.findOne(_id: transactionId, { _id: 1}))
console.log("Already in db");
else
Transaction.insert(transaction)
为加快速度,我将整个集合加载到内存中,并且仅在脚本末尾插入。
const toInsert = [];
const transactions = await Transaction.find().select(_id);
// I transform array of transactions to an object of transaction where key is _id, thus i can avoid using a find at every iteration
const transactionsObject = transactions.reduce((obj, transaction) => {
obj[transaction.id] = true;
return obj;
}, {})
for (transaction in transactions) {
if (transactionsObject[transactionId])
console.log("Already in db");
else {
toInsert.push(transaction);
transactionsObject[transaction._id] = true;
}
Transaction.insertMany(toInsert);
使用此脚本版本,该脚本非常快,但是我对可伸缩性有所担心,因为我将需要大量内存,并且如果需要对此进行线程化,则需要在线程之间共享所有内容。
您如何优化脚本?
答案 0 :(得分:0)
让mongo计算丢失的文档可能很有意义。一种优化是仅找到您关心的交易的ID:
const transactionIds = await Transaction.find({_id: {$in: listOfIds}}).select(_id);
一种更快的替代方法是在id上创建唯一索引(默认的_id字段已经具有唯一索引),然后尝试使用{ordered: false}
插入所有文档。某些插入操作将失败,但是让mongo进行计算会更快。
在将[set]设置为false的情况下,插入操作[将]对所有剩余文档继续进行。 https://docs.mongodb.com/manual/reference/method/db.collection.insertMany/
> db.test.insertMany([{_id: 1}], { ordered: false })
{ "acknowledged" : true, "insertedIds" : [ 1 ] }
> db.test.insertMany([{_id: 1}, {_id: 2}], { ordered: false })
2019-07-03T16:25:54.402+0000 E QUERY [js] BulkWriteError: write error at item 0 in bulk operation :
BulkWriteError({
"writeErrors" : [
{
"index" : 0,
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: test.test index: _id_ dup key: { : 1.0 }",
"op" : {
"_id" : 1
}
}
],
"writeConcernErrors" : [ ],
"nInserted" : 1,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})