MongoDB索引的操作和優化索引查詢


1. mongodb索引

  • 索引通常能夠極大的提高查詢的效率, 如果沒有索引, MongoDB在讀取數據時必須掃描集合中的每個文件並選取那些符合查詢條件的記錄.
  • 這種掃描全集合的查詢效率是非常低的, 特別在處理大量的數據時, 查詢可以要花費幾十秒甚至幾分鍾, 這對網站的性能是非常致命的.
  • 索引是特殊的數據結構, 索引存儲在一個易於遍歷讀取的數據集合中, 索引是對數據庫表中一列或多列的值進行排序的一種結構.

2. 索引的增刪查改

2.1 增加索引

# 1是正排, -1 是倒排
db.$collection.createIndex({$field:1});

# 建立復合索引
db.$collection.createIndex({$field1:1, $field2:1});

2.2 查看索引

db.$collection.getIndexes();

2.3 刪除索引

db.$collecion.dropIndex($index);

# 刪除全部索引
db.$collection.dropIndexes();

3. 創建索引前后的對比

4. explain的參數詳解以及重點查看參數

{
	"queryPlanner" : {   // 查詢計划
		"plannerVersion" : 1,   
 		"namespace" : "test.testindex",  // 查詢的表名
		"indexFilterSet" : false,  //針對該query是否有indexfilter ???
		"parsedQuery" : {  // 查詢條件
			"id" : {
				"$lt" : 97
			}
		},
		"winningPlan" : {// 查詢優化器針對該query所返回的最優執行計划的詳細內容。
			"stage" : "FETCH",// FETC: 可以理解為通過返回的index位置去檢索具體的文檔???
			"inputStage" : {
				"stage" : "IXSCAN",  // 表示進行的是index scanning
				"keyPattern" : { // 索引內容
					"id" : 1,
					"age" : 1
				},
				"indexName" : "id_1_age_1", // 使用的索引
				"isMultiKey" : false, // 非復合索引 如果索引建立在array上,此處將是true
				"multiKeyPaths" : {
					"id" : [ ],
					"age" : [ ]
				},
				"isUnique" : false,
				"isSparse" : false,
				"isPartial" : false,
				"indexVersion" : 2,
				"direction" : "forward", //此query的查詢順序,此處是forward,如果用了.sort({$filed:-1})將顯示backward
				"indexBounds" : {
					"id" : [
						"[-inf.0, 97.0)" // winningplan所掃描的索引范圍 $lt:97
					],
					"age" : [
						"[MinKey, MaxKey]"
					]
				}
			}
		},
		"rejectedPlans" : [ ]
	},
	"executionStats" : {
		"executionSuccess" : true,  // 是否執行成功
		"nReturned" : 97,    // 結果返回條數
		"executionTimeMillis" : 0, // 整體執行時間 單位毫秒
		"totalKeysExamined" : 97, // 索引掃描個數
		"totalDocsExamined" : 97, // 文檔掃描個數
		"executionStages" : {
			"stage" : "FETCH",
			"nReturned" : 97, 
			"executionTimeMillisEstimate" : 0, //該查詢根據index去檢索document獲取97條具體數據的時間
			"works" : 98,
			"advanced" : 97,
			"needTime" : 0,
			"needYield" : 0,
			"saveState" : 0,
			"restoreState" : 0,
			"isEOF" : 1,
			"invalidates" : 0,
			"docsExamined" : 97,
			"alreadyHasObj" : 0,
			"inputStage" : {
				"stage" : "IXSCAN",
				"nReturned" : 97, //最理想狀態 nReturned=totalKeysExamined & totalDocsExamined=0 / nReturned=totalKeysExamined=totalDocsExamined
				"executionTimeMillisEstimate" : 0,//該查詢掃描97行index所用時間
				"works" : 98,
				"advanced" : 97,
				"needTime" : 0,
				"needYield" : 0,
				"saveState" : 0,
				"restoreState" : 0,
				"isEOF" : 1,
				"invalidates" : 0,
				"keyPattern" : {
					"id" : 1,
					"age" : 1
				},
				"indexName" : "id_1_age_1",
				"isMultiKey" : false,
				"multiKeyPaths" : {
					"id" : [ ],
					"age" : [ ]
				},
				"isUnique" : false,
				"isSparse" : false,
				"isPartial" : false,
				"indexVersion" : 2,
				"direction" : "forward",
				"indexBounds" : {
					"id" : [
						"[-inf.0, 97.0)"
					],
					"age" : [
						"[MinKey, MaxKey]"
					]
				},
				"keysExamined" : 97,
				"seeks" : 1,
				"dupsTested" : 0,
				"dupsDropped" : 0,
				"seenInvalidated" : 0
			}
		}
	},
	"serverInfo" : {
		"host" : "centos01",
		"port" : 27017,
		"version" : "4.0.9",
		"gitVersion" : "fc525e2d9b0e4bceff5c2201457e564362909765"
	},
	"ok" : 1
}
  • stage的含義
  • COLLSCAN
    全表掃描
  • XSCAN
    索引掃描
  • ETCH
    根據索引去檢索指定document
  • SHARD_MERGE
    將各個分片返回數據進行merge, 但是根據源碼中的信息,個人還總結了文檔中沒有的如下幾類(常用如下,由於是通過源碼查找,可能有所遺漏)
  • SORT
    表明在內存中進行了排序(與老版本的scanAndOrder:true一致)
  • LIMIT
    使用limit限制返回數
  • SKIP
    使用skip進行跳過
  • IDHACK
    針對_id進行查詢
  • SHARDING_FILTER
    通過mongos對分片數據進行查詢
  • COUNT
    利用db.coll.explain().count()之類進行count運算
  • COUNTSCAN
    count不使用用Index進行count時的stage返回
  • COUNT_SCAN
    count使用了Index進行count時的stage返回
  • SUBPLA
    未使用到索引的$or查詢的stage返回
  • TEXT
    使用全文索引進行查詢時候的stage返回
  • PROJECTION
    限定返回字段時候stage的返回
  • 我們希望看到的stage的組合

Fetch+IDHACK
Fetch+ixscan
Limit+(Fetch+ixscan)
PROJECTION+ixscan
SHARDING_FILTER+ixscan

  • 不希望看到包含如下的stage:

COLLSCAN(全表掃),SORT(使用sort但是無index),不合理的SKIP,SUBPLA(未用到index的$or)
對於count查詢,希望看到的有:
COUNT_SCAN

不希望看到的有:
COUNTSCAN

5. Explain 分析實例

5.1 准備數據

{ "_id" : ObjectId("5ce371f2d9a9a24e713bedd3"), "a" : 0, "b" : 38, "c" : 10 }
{ "_id" : ObjectId("5ce371f2d9a9a24e713bedd4"), "a" : 1, "b" : 64, "c" : 9 }
{ "_id" : ObjectId("5ce371f2d9a9a24e713bedd5"), "a" : 2, "b" : 28, "c" : 8 }
{ "_id" : ObjectId("5ce371f2d9a9a24e713bedd6"), "a" : 0, "b" : 73, "c" : 7 }
{ "_id" : ObjectId("5ce371f2d9a9a24e713bedd7"), "a" : 1, "b" : 95, "c" : 6 }
{ "_id" : ObjectId("5ce371f2d9a9a24e713bedd8"), "a" : 2, "b" : 11, "c" : 5 }
{ "_id" : ObjectId("5ce371f2d9a9a24e713bedd9"), "a" : 0, "b" : 73, "c" : 4 }
{ "_id" : ObjectId("5ce371f2d9a9a24e713bedda"), "a" : 1, "b" : 14, "c" : 3 }
{ "_id" : ObjectId("5ce371f2d9a9a24e713beddb"), "a" : 2, "b" : 73, "c" : 2 }
{ "_id" : ObjectId("5ce371f2d9a9a24e713beddc"), "a" : 0, "b" : 0, "c" : 1 }
  • 測試的查詢語句

db.testindex.find({a:2,b:{$lt:90}}).sort({c:-1});

5.2 沒有index的查詢計划

db.testindex.find({a:2,b:{$lt:90}}).sort({c:-1}).explain("executionStats");
{
	"executionStats" : {
		"executionSuccess" : true,
		"nReturned" : 3,
		"executionTimeMillis" : 0,
		"totalKeysExamined" : 0,
		"totalDocsExamined" : 10,
		"executionStages" : {
			"stage" : "SORT",
			"nReturned" : 3,
			"executionTimeMillisEstimate" : 0,
			"works" : 17,
			"advanced" : 3,
			"needTime" : 13,
			"needYield" : 0,
			"saveState" : 0,
			"restoreState" : 0,
			"isEOF" : 1,
			"invalidates" : 0,
			"sortPattern" : {
				"c" : -1
			},
			"memUsage" : 153,
			"memLimit" : 33554432,
			"inputStage" : {
				"stage" : "SORT_KEY_GENERATOR",
				"nReturned" : 3,
				"executionTimeMillisEstimate" : 0,
				"works" : 13,
				"advanced" : 3,
				"needTime" : 9,
				"needYield" : 0,
				"saveState" : 0,
				"restoreState" : 0,
				"isEOF" : 1,
				"invalidates" : 0,
				"inputStage" : {
					"stage" : "COLLSCAN",
					"filter" : {
						"$and" : [
							{
								"a" : {
									"$eq" : 2
								}
							},
							{
								"b" : {
									"$lt" : 90
								}
							}
						]
					},
					"nReturned" : 3,
					"executionTimeMillisEstimate" : 0,
					"works" : 12,
					"advanced" : 3,
					"needTime" : 8,
					"needYield" : 0,
					"saveState" : 0,
					"restoreState" : 0,
					"isEOF" : 1,
					"invalidates" : 0,
					"direction" : "forward",
					"docsExamined" : 10
				}
			}
		}
	},
	"serverInfo" : {
		"host" : "centos01",
		"port" : 27017,
		"version" : "4.0.9",
		"gitVersion" : "fc525e2d9b0e4bceff5c2201457e564362909765"
	},
	"ok" : 1
}

  • nReturned為3,符合的條件的返回為3條.

  • totalKeysExamined為0,沒有使用index.

  • totalDocsExamined為10,掃描了所有記錄.

  • executionStages.stage為SORT,未使用index的sort,占用的內存與內存限制為”memUsage” : 153, “memLimit” : 33554432.

  • executionStages.inputStage.stage為COLLSCAN,全表掃描,掃描條件為

{
    "filter":{
        "$and":[
            {
                "a":{
                    "$eq":2
                }
            },
            {
                "b":{
                    "$lt":90
                }
            }
        ]
    }
}

5.3 增加字段c的索引

db.testindex.createIndex({c:1});
{
	"executionStats" : {
		"executionSuccess" : true,
		"nReturned" : 3,
		"executionTimeMillis" : 0,
		"totalKeysExamined" : 10,
		"totalDocsExamined" : 10,
		"executionStages" : {
			"stage" : "FETCH",
			"filter" : {
				"$and" : [
					{
						"a" : {
							"$eq" : 2
						}
					},
					{
						"b" : {
							"$lt" : 90
						}
					}
				]
			},
			"nReturned" : 3,
			"executionTimeMillisEstimate" : 0,
			"works" : 11,
			"advanced" : 3,
			"needTime" : 7,
			"needYield" : 0,
			"saveState" : 0,
			"restoreState" : 0,
			"isEOF" : 1,
			"invalidates" : 0,
			"docsExamined" : 10,
			"alreadyHasObj" : 0,
			"inputStage" : {
				"stage" : "IXSCAN",
				"nReturned" : 10,
				"executionTimeMillisEstimate" : 0,
				"works" : 11,
				"advanced" : 10,
				"needTime" : 0,
				"needYield" : 0,
				"saveState" : 0,
				"restoreState" : 0,
				"isEOF" : 1,
				"invalidates" : 0,
				"keyPattern" : {
					"c" : 1
				},
				"indexName" : "c_1",
				"isMultiKey" : false,
				"multiKeyPaths" : {
					"c" : [ ]
				},
				"isUnique" : false,
				"isSparse" : false,
				"isPartial" : false,
				"indexVersion" : 2,
				"direction" : "backward",
				"indexBounds" : {
					"c" : [
						"[MaxKey, MinKey]"
					]
				},
				"keysExamined" : 10,
				"seeks" : 1,
				"dupsTested" : 0,
				"dupsDropped" : 0,
				"seenInvalidated" : 0
			}
		}
	},
	"serverInfo" : {
		"host" : "centos01",
		"port" : 27017,
		"version" : "4.0.9",
		"gitVersion" : "fc525e2d9b0e4bceff5c2201457e564362909765"
	},
	"ok" : 1
}


我們發現,Stage沒有了SORT,因為我們sort字段有了index,但是由於查詢還是沒有index,故totalDocsExamined還是10,但是由於sort用了index,totalKeysExamined也是10,但是僅對sort排序做了優化,查詢性能還是一樣的低效。

5.4 使用db.testindex.ensureIndex({b:1,a:1,c:1})索引的執行計划

{
    "executionStats" : {
		"executionSuccess" : true,
		"nReturned" : 3,
		"executionTimeMillis" : 0,
		"totalKeysExamined" : 10,
		"totalDocsExamined" : 10,
		"executionStages" : {
			"stage" : "FETCH",
			"filter" : {
				"$and" : [
					{
						"a" : {
							"$eq" : 2
						}
					},
					{
						"b" : {
							"$lt" : 90
						}
					}
				]
			},
			"nReturned" : 3,
			"executionTimeMillisEstimate" : 0,
			"works" : 12,
			"advanced" : 3,
			"needTime" : 7,
			"needYield" : 0,
			"saveState" : 0,
			"restoreState" : 0,
			"isEOF" : 1,
			"invalidates" : 0,
			"docsExamined" : 10,
			"alreadyHasObj" : 0,
			"inputStage" : {
				"stage" : "IXSCAN",
				"nReturned" : 10,
				"executionTimeMillisEstimate" : 0,
				"works" : 11,
				"advanced" : 10,
				"needTime" : 0,
				"needYield" : 0,
				"saveState" : 0,
				"restoreState" : 0,
				"isEOF" : 1,
				"invalidates" : 0,
				"keyPattern" : {
					"c" : 1
				},
				"indexName" : "c_1",
				"isMultiKey" : false,
				"multiKeyPaths" : {
					"c" : [ ]
				},
				"isUnique" : false,
				"isSparse" : false,
				"isPartial" : false,
				"indexVersion" : 2,
				"direction" : "backward",
				"indexBounds" : {
					"c" : [
						"[MaxKey, MinKey]"
					]
				},
				"keysExamined" : 10,
				"seeks" : 1,
				"dupsTested" : 0,
				"dupsDropped" : 0,
				"seenInvalidated" : 0
			}
		}
	}

這個索引被拒絕了, 用的還是c_1的索引.

5.5 使用db.testindex.ensureIndex({a:1,b:1,c:1})索引的執行計划

{
    "executionStats" : {
		"executionSuccess" : true,
		"nReturned" : 3,
		"executionTimeMillis" : 0,
		"totalKeysExamined" : 3,
		"totalDocsExamined" : 3,
		"executionStages" : {
			"stage" : "SORT",
			"nReturned" : 3,
			"executionTimeMillisEstimate" : 0,
			"works" : 10,
			"advanced" : 3,
			"needTime" : 5,
			"needYield" : 0,
			"saveState" : 0,
			"restoreState" : 0,
			"isEOF" : 1,
			"invalidates" : 0,
			"sortPattern" : {
				"c" : 1
			},
			"memUsage" : 153,
			"memLimit" : 33554432,
			"inputStage" : {
				"stage" : "SORT_KEY_GENERATOR",
				"nReturned" : 3,
				"executionTimeMillisEstimate" : 0,
				"works" : 5,
				"advanced" : 3,
				"needTime" : 1,
				"needYield" : 0,
				"saveState" : 0,
				"restoreState" : 0,
				"isEOF" : 1,
				"invalidates" : 0,
				"inputStage" : {
					"stage" : "FETCH",
					"nReturned" : 3,
					"executionTimeMillisEstimate" : 0,
					"works" : 4,
					"advanced" : 3,
					"needTime" : 0,
					"needYield" : 0,
					"saveState" : 0,
					"restoreState" : 0,
					"isEOF" : 1,
					"invalidates" : 0,
					"docsExamined" : 3,
					"alreadyHasObj" : 0,
					"inputStage" : {
						"stage" : "IXSCAN",
						"nReturned" : 3,
						"executionTimeMillisEstimate" : 0,
						"works" : 4,
						"advanced" : 3,
						"needTime" : 0,
						"needYield" : 0,
						"saveState" : 0,
						"restoreState" : 0,
						"isEOF" : 1,
						"invalidates" : 0,
						"keyPattern" : {
							"a" : 1,
							"b" : 1,
							"c" : 1
						},
						"indexName" : "a_1_b_1_c_1",
						"isMultiKey" : false,
						"multiKeyPaths" : {
							"a" : [ ],
							"b" : [ ],
							"c" : [ ]
						},
						"isUnique" : false,
						"isSparse" : false,
						"isPartial" : false,
						"indexVersion" : 2,
						"direction" : "forward",
						"indexBounds" : {
							"a" : [
								"[2.0, 2.0]"
							],
							"b" : [
								"[-inf.0, 90.0)"
							],
							"c" : [
								"[MinKey, MaxKey]"
							]
						},
						"keysExamined" : 3,
						"seeks" : 1,
						"dupsTested" : 0,
						"dupsDropped" : 0,
						"seenInvalidated" : 0
					}
				}
			}
		}
	},
	"serverInfo" : {
		"host" : "centos01",
		"port" : 27017,
		"version" : "4.0.9",
		"gitVersion" : "fc525e2d9b0e4bceff5c2201457e564362909765"
	},
	"ok" : 1
}

我們可以看到

  • nReturned為3,返回3條記錄
  • totalKeysExamined為3,掃描了3個index
  • totalDocsExamined為3,掃描了3個docs
  • 此時nReturned=totalDocsExamined=totalKeysExamined,符合我們的期望。看起來很美吧?
  • 但是,但是,但是!重要的事情說三遍!executionStages.Stage為Sort,在內存中進行排序了,這個在生產環境中尤其是在數據量較大的時候,是非常消耗性能的,這個千萬不能忽視了,我們需要改進這個點。

5.6 使用db.testindex.ensureIndex({a:1, c:1, b:1)

{ 
    "executionStats" : {
		"executionSuccess" : true,
		"nReturned" : 3,
		"executionTimeMillis" : 0,
		"totalKeysExamined" : 3,
		"totalDocsExamined" : 3,
		"executionStages" : {
			"stage" : "FETCH",
			"nReturned" : 3,
			"executionTimeMillisEstimate" : 0,
			"works" : 5,
			"advanced" : 3,
			"needTime" : 0,
			"needYield" : 0,
			"saveState" : 0,
			"restoreState" : 0,
			"isEOF" : 1,
			"invalidates" : 0,
			"docsExamined" : 3,
			"alreadyHasObj" : 0,
			"inputStage" : {
				"stage" : "IXSCAN",
				"nReturned" : 3,
				"executionTimeMillisEstimate" : 0,
				"works" : 4,
				"advanced" : 3,
				"needTime" : 0,
				"needYield" : 0,
				"saveState" : 0,
				"restoreState" : 0,
				"isEOF" : 1,
				"invalidates" : 0,
				"keyPattern" : {
					"a" : 1,
					"c" : 1,
					"b" : 1
				},
				"indexName" : "a_1_c_1_b_1",
				"isMultiKey" : false,
				"multiKeyPaths" : {
					"a" : [ ],
					"c" : [ ],
					"b" : [ ]
				},
				"isUnique" : false,
				"isSparse" : false,
				"isPartial" : false,
				"indexVersion" : 2,
				"direction" : "forward",
				"indexBounds" : {
					"a" : [
						"[2.0, 2.0]"
					],
					"c" : [
						"[MinKey, MaxKey]"
					],
					"b" : [
						"[-inf.0, 90.0)"
					]
				},
				"keysExamined" : 3,
				"seeks" : 1,
				"dupsTested" : 0,
				"dupsDropped" : 0,
				"seenInvalidated" : 0
			}
		}
	},
	"serverInfo" : {
		"host" : "centos01",
		"port" : 27017,
		"version" : "4.0.9",
		"gitVersion" : "fc525e2d9b0e4bceff5c2201457e564362909765"
	},
	"ok" : 1
}

我們可以看到

  • nReturned為3,返回3條記錄
  • totalKeysExamined為3,掃描了3個index
  • totalDocsExamined為3,掃描了3個docs
  • nReturned=totalKeysExamined=totalDocsExamined,Stage無Sort,即利用了index進行排序,而非內存,這個性能的提升高於多掃幾個index的代價。
  • 綜上可以有一個小結論,當查詢覆蓋精確匹配,范圍查詢與排序的時候,
    精確匹配字段,排序字段,范圍查詢字段這樣的索引排序會更為高效。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM