我在使用mongo时遇到了一些性能问题。
我有这样的收藏:
{
"_id" : ObjectId,
"status" : String,
"song" : ObjectId,
"room" : ObjectId,
"duration" : Number,
"order" : 0,
"addedAt" : ISODate("2016-02-09T14:16:21.331Z"),
"startedAt" : ISODate("2016-02-09T14:16:21.393Z")
}其中我有以下索引:
/* 1 */
{
"0" : {
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "mydb.mycollection"
},
"1" : {
"v" : 1,
"key" : {
"song" : 1
},
"name" : "song_1",
"ns" : "mydb.mycollection",
"background" : true,
"safe" : null
},
"2" : {
"v" : 1,
"key" : {
"user" : 1
},
"name" : "user_1",
"ns" : "mydb.mycollection",
"background" : true,
"safe" : null
},
"3" : {
"v" : 1,
"key" : {
"room" : 1
},
"name" : "room_1",
"ns" : "mydb.mycollection",
"background" : true,
"safe" : null
},
"4" : {
"v" : 1,
"key" : {
"duration" : 1
},
"name" : "duration_1",
"ns" : "mydb.mycollection",
"background" : true,
"safe" : null
}
}该集合中有超过300万条记录。
现在,Mongo在日志中显示了这个缓慢的查询信息(为了可读性而缩进):
2016-02-11T11:07:47.897+0000 [conn19] query mydb.mycollection query: {
orderby: { startedAt: -1 },
$query: { status: {$in: [ "ended", "skipped" ] }, room: ObjectId('myroomid') } }
planSummary: IXSCAN {room: 1 }, IXSCAN { room: 1 } cursorid:64767933277
noreturn:10
ntoskip:0
nscanned:41663
nscannedObjects:41663 keyUpdates:0
numYields:4 locks(micros) r:2949888
nreturned:10 realen:2668
1737ms如您所见,执行时间为1737ms (有时甚至更长),我也遇到过CPU利用率过高的问题。
有人知道为什么吗?有什么需要添加的索引吗?是不是3M记录的数据太多?
谢谢!
发布于 2016-02-11 20:07:19
尽管有一些索引交叉点在这里和一般情况下都不适用,但一个好的经验法则是
MongoDB对每个查询仅使用一个索引。
因此,您的查询针对两个字段(status和room),并按另一个字段(startedAt)排序。所使用的查询计划清楚地表明它只使用room上的索引。对于所有其他值,它读取与查询的room部分匹配的文档,如nscanned和nscannedObjects所示。
为了充分利用这里的索引,您需要一个针对room、status和startedAt的复合索引。请注意,顺序很重要,因此,如果您的查询如下所示:
db.rooms.find({
room: someRoomId,
status: {$in: [ "ended", "skipped" ]
}).sort({startedAt:-1})相应的索引应该是
db.rooms.createIndex({room:1,status:1,startedAt:-1})如果您的查询看起来像
db.rooms.find({
status: {$in: [ "ended", "skipped" ],
room: someRoomId
}).sort({startedAt:-1})你的索引应该是
db.rooms.createIndex({status:1,room:1,startedAt:-1})相应地设置索引后,您的查询速度应该会快很多。
附注
在您的示例中,您将ObjectId与字符串值一起使用。这完全说不通。您可以直接使用在那里使用的字符串(比如房间号),也可以使用new ObjectId()返回的ObjectId()。当字段的基数足够高时(例如,由房间号提供),则不需要使用ObjectId(),因为同一建筑物中不太可能有两个房间具有相同的房间号。
发布于 2016-02-11 20:02:16
numYields:4 locks(micros) r:2949888看起来并不好。
它基本上是说,查询被中断了4次,以便让其他操作完成。
发布于 2016-02-11 19:53:51
添加startedAt降序索引(-1)。如果您使用or,请首先选择最大集。如果使用and,请首先选择最小的集合。这也会有所帮助。
所以你有room: ObjectId('myroomid'),这应该在status: {$in: [ "ended", "skipped" ] }之前。
我假设room: ObjectId('myroomid')计数小于status: {$in: [ "ended", "skipped" ] }计数。
https://stackoverflow.com/questions/35338448
复制相似问题