它没有直接回答这个问题,而是给出了一些关于
进行单独查询以获取书籍列表,然后获取每本书的页数
部分。这并不总是一件坏事。Mongodb 在简单查询中非常有效,因此我给您一些数字来考虑单个 $lookup 管道与多个查询的性能,并鼓励您在数据集上测试典型查询。如果您不需要一次获取所有数据,则电子分页可以产生巨大的影响。
设置
一个包含 100 个用户 X 1,000 本书 X 1,000 页的小型数据库,每个用户在一个微型 1 vCPU / 2 GB 内存 / 50 GB 磁盘 / LON1 - Ubuntu MongoDB 3.4.10 on 16.04 droplet。
pages
集合创建如下:
for USERID in {1..100}; do
echo "" > pages.json;
for BOOKID in {1..1000}; do
./node_modules/.bin/mgeneratejs "{\"bookId\": \"$USERID-$BOOKID\", \"name\": {\"\$sentence\":{\"words\":3}}, \"description\": \"\$paragraph\"}" -n 1000 >> pages.json
done
cat pages.json | mongoimport -d so -c pages
done
一个books
几乎是一样的。
基本数据:
db.books.stats(1024*1024)
"ns" : "so.books",
"size" : 50,
"count" : 100000,
"avgObjSize" : 533,
"storageSize" : 52,
"nindexes" : 2,
"totalIndexSize" : 1,
"indexSizes" : {
"_id_" : 0,
"userId_1" : 0
},
db.pages.stats(1024*1024)
"ns" : "so.pages",
"size" : 51673,
"count" : 100000000,
"avgObjSize" : 541,
"storageSize" : 28920,
"nindexes" : 2,
"totalIndexSize" : 1424,
"indexSizes" : {
"_id_" : 994,
"bookId_1" : 430
},
$查找
@chridam 回答的管道
db.books.aggregate([
{ "$match": { "userId": 18 } },
{ "$lookup": {
"from": "pages",
"localField": "_id",
"foreignField": "bookId",
"as": "pageCount"
}},
{ "$addFields": {
"pageCount": { "$size": "$pageCount" }
}}
])
提供极快的响应:
"op" : "command",
"command" : {
"aggregate" : "books"
},
"keysExamined" : 1000,
"docsExamined" : 1000,
"nreturned" : 101,
"responseLength" : 57234,
"millis" : 1028
对于前 100 个文档,让您在一秒钟内开始处理文档。
整个事情的总时间:
db.books.aggregate([
{ "$match": { "userId": 18 } },
{ "$lookup": {
"from": "pages",
"localField": "_id",
"foreignField": "bookId",
"as": "pageCount"
}},
{ "$addFields": {
"pageCount": { "$size": "$pageCount" }
}}
]).toArray()
再增加 8 秒:
"op" : "getmore",
"query" : {
"getMore" : NumberLong("32322423895"),
"collection" : "books"
},
"keysExamined" : 0,
"docsExamined" : 0,
"nreturned" : 899,
"responseLength" : 500060,
"millis" : 8471
检索所有数据的总时间超过 9 秒
多个查询
检索书籍:
let bookIds = [];
db.books.find({userId:12}).forEach(b=>{bookIds.push(b._id);});
在 10 毫秒内填充数组:
"op" : "query",
"query" : {
"find" : "books",
"filter" : {
"userId" : 34
}
},
"keysExamined" : 101,
"docsExamined" : 101,
"nreturned" : 101,
"responseLength" : 54710,
"millis" : 3
和
"op" : "getmore",
"query" : {
"getMore" : NumberLong("34224552674"),
"collection" : "books"
},
"keysExamined" : 899,
"docsExamined" : 899,
"nreturned" : 899,
"responseLength" : 485698,
"millis" : 7
计算页数:
db.pages.aggregate([
{ $match: { bookId: { $in: bookIds } } },
{ $group: { _id: "$bookId", cnt: { $sum: 1 } } }
]).toArray()
总共需要 1.5 秒:
"op" : "command",
"command" : {
"aggregate" : "pages"
},
"keysExamined" : 1000001,
"docsExamined" : 0,
"nreturned" : 101,
"responseLength" : 3899,
"millis" : 1574
和
"op" : "getmore",
"query" : {
"getMore" : NumberLong("58311204806"),
"collection" : "pages"
},
"keysExamined" : 0,
"docsExamined" : 0,
"nreturned" : 899,
"responseLength" : 34935,
"millis" : 0
合并结果
不是查询,但应该在应用程序级别完成。在 mongoshell javascript 中需要几毫秒,这使得检索所有数据的总时间少于 2 秒。