mongodb利用索引對find結果排序(sort)
最近線上有個接口超時報警,排查發現是查詢MongoDB的時候比較慢(平均耗時1s以上)。文檔結構很簡單:
{
"_id" : NumberLong(1214789),
"created_at" : ISODate("2019-02-21T16:08:44.337Z"),
"updated_at" : ISODate("2019-02-21T16:08:44.337Z"),
"cid" : 158865,
"uid" : NumberLong(1024654707),
"object_id" : "",
"random" : 163595854,
"participation_num" : 1
}
已有索引:
{"_id":1}
{"uid":1}
{"cid":1}
{"cid":1,"uid":1}
接口里的查詢轉換成查詢語句為:
db.record.find({
"cid":158865,
"is_del": {
"$ne": true
}
}).sort({"_id": -1}).limit(10)
數據量100W+,不小,但是對MongoDB來說應該也不算大。
想當然的覺得:已經有cid索引,那么find時走索引應該查起來很快,排序的話,_id也是有索引的,應該不會出現問題啊。后經老大指點,MongoDB的索引應該和mysql一樣只能用一個(事實上2.6版本開始mongo是可以一個查詢使用多個索引的)。
既然是只能用一個索引,那當然想着把sort里的字段也加一起弄一個復合索引:
{"cid":1,"cid":-1}
加上索引后這里explain里可以看到:
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"$not" : {
"is_del" : {
"$eq" : true
}
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"cid" : 1,
"_id" : -1
},
"indexName" : "cid_id",
"isMultiKey" : false,
"direction" : "forward",
"indexBounds" : {
"cid" : [
"[158865.0, 158865.0]"
],
"_id" : [
"[MaxKey, MinKey]"
]
}
}
}
這里查詢出來的實際上就直接是排好序的了
這里總結下mongo的索引一些要注意的地方:
-
避免內存排序,內存排序超過32M時會放棄。這里有個有意思的地方:
對於文檔:
{
"_id":1111,
"a":2222,
"b":3333
}
有如下三個索引:
{"_id":1}
{"a":1}
當使用如下查詢時,在數據量不同時,mongo選擇的查詢方案是不同的:
db.testdoc.find({"a":2222}).sort({"_id":-1}).limit(10)
當數據量少時,會使用{"_a":1}
索引,並進行內存排序,當數據量大時,會使用{"_id":1}
索引,這個時候find
里的a並沒有用到索引,所以最終是會掃描所有文檔,速度非常慢(上面線上接口慢其實也是這樣的情況).
- 索引里的排序順序要和sort里的完全一致或完全相反:
{"a":-1,"b":1}
能用於{"a":-1,"b":1}
和{"a":1,"b":-1}