索引的操作
資料庫百分之八十的工作基本上都是查詢,而索引能幫我們更快的查詢到想要的資料.但是其降低了資料的寫入速度,所以要權衡常用的查詢欄位,不必在太多欄位上建立索引.
在mongoDB中預設是用btree來組織索引檔案,並且可以按欄位升序/降序來建立,便於排序. 資料準備
for (var i = 1; i <100000; i++) { db.test.insert({name:'user'+i,num:i,sn:Math.floor(Math.random()*10000000)})}
索引常用操作
查看當前集合的索引
> db.test.getIndexes();[ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.test" }]
MongoDB有個預設的_id的鍵,他相當於“主鍵”的角色。集合建立後系統會自動建立一個索引在_id鍵上,它是預設索引,索引名叫“_id”,是無法被刪除的。
另外, system.indexes集合中包含了每個索引的詳細資料,因此可以通過下面的命令查詢已經存在的索引
db.system.indexes.find({});
建立單列索引
db.collection.ensureIndex({field:1/-1}) # 1是正序,-1是倒序
建立多列索引(複合式索引)
db.collection.ensureIndex({field1:1/-1, field2:1/-1})
在大多數情況下我們建立的索引都是多列索引,因為資料庫查詢器只會選擇最優的索引來進行查詢,在多列分別建立索引,查詢器只會選擇其中一列索引來進行查詢,而直接建立一個多列索引的話,該索引由於是作用於多列的,效率更高於單列索引,具體多列索引建立技巧可以查看下文中的 <<新版Explain的分析執行個體>>,另外,mongoDB的多列索引也遵循著最左首碼的原則
db.test.ensureIndex({"username":1, "age":-1})
該索引被建立後,基於username和age的查詢將會用到該索引,或者是基於username的查詢也會用到該索引,但是只是基於age的查詢將不會用到該複合索引。因此可以說,如果想用到複合索引,必須在查詢條件中包含複合索引中的前N個索引列。然而如果查詢條件中的索引值順序和複合索引中的建立順序不一致的話,MongoDB可以智能的協助我們調整該順序,以便使複合索引可以為查詢所用
db.test.find({"age": 30, "username": "stephen"})
對於上面樣本中的查詢條件,MongoDB在檢索之前將會動態調整查詢條件文檔的順序,以使該查詢可以用到剛剛建立的複合索引。 建立子文檔索引
db.collection.ensureIndex({'filed.subfield':1/-1});
建立唯一索引
db.collection.ensureIndex({filed:1/-1}, {unique:true});
建立稀疏索引
稀疏索引的特點------如果針對field做索引,針對不含field列的文檔,將不建立索引.
與之相對,普通索引,會把該文檔的field列的值認為NULL,並建索引.
適宜於: 小部分文檔含有某列時.
db.tea.find();{ "_id" : ObjectId("5275f99b87437c610023597b"), "email" : "a@163.com" }{ "_id" : ObjectId("5275f99e87437c610023597c"), "email" : "b@163.com" }{ "_id" : ObjectId("5275f9e887437c610023597e"), "email" : "c@163.com" }{ "_id" : ObjectId("5275fa3887437c6100235980") }
db.collection.ensureIndex({field:1/-1},{sparse:true});
如上內容,最後一行沒有email列,如果分別加普通索引,和稀疏索引,對於最後一行的email分別當成null 和 忽略最後一行來處理.根據{email:null}來查詢,前者能利用到索引,而後者用不到索引,是一個全表掃描的過程; 建立雜湊索引
雜湊索引速度比普通索引快,但是,無能對範圍查詢進行最佳化.
適宜於隨機性強的散列
db.collection.ensureIndex({file:’hashed’});
重建索引
一個表經過很多次修改後,導致表的檔案產生空洞,索引檔案也如此.可以通過索引的重建,減少索引檔案片段,並提高索引的效率.類似mysql中的optimize table
db.collection.reIndex()
刪除索引
db.collection.dropIndex({filed:1/-1}); #刪除單個索引db.collection.dropIndexes(); #刪除所有索引
Regex在索引中的應用
Regex可以靈活地匹配查詢條件,如果希望Regex能命中索引,就要注意了:
Mongodb能為首碼型的Regex命中索引(和mysql一樣),比如:需要查詢Mail中user以z開頭的:/^z/
如果有user索引,這種查詢很高效,但其他的即使有索引,也不會命中索引,比說:需要查詢Mail中的user中含有z的:
/.*z.*//^.*z.*/
這種查詢是不會命中到索引的,當資料量很大,速度很慢
總之,^後的條件必須明確,不能^.* ^[a-z]之類開頭的 查詢計劃explain
我學習mongodb比較晚,安裝的是3.05版本的,發現此版本的explain的使用方法跟教程上有很大不同,究竟是從什麼版本開始發生改變的,我也就不去追溯了. 新版explain介紹
新版本的explain有三種模式,作為explain的參數傳進去 queryPlanner 預設 executionStats allPlansExecution queryPlanner
queryPlanner是現版本explain的預設模式,queryPlanner模式下並不會去真正進行query語句查詢,而是針對query語句進行執行計畫分析並選出winning plan。
{ "queryPlanner": { "plannerVersion": 1, "namespace": "game_db.game_user", "indexFilterSet": false,//針對該query是否有indexfilter(詳見下文) "parsedQuery": { "w": { "$eq": 1 } }, "winningPlan": { // 查詢最佳化工具針對該query所返回的最優執行計畫的詳細內容。 "stage": "FETCH", //最優執行計畫的stage,這裡返回是FETCH,可以理解為通過返回的index位置去檢索具體的文檔(詳見下文) "inputStage": { // 上一個stage的child stage,此處是IXSCAN,表示進行的是index scanning。 "stage": "IXSCAN", "keyPattern": { "w": 1, //所掃描的index內容 "n": 1 // 返回的條數? }, "indexName": "w_1_n_1", //索引名稱 "isMultiKey": false, //是否是Multikey,此處返回是false,如果索引建立在array上,此處將是true "direction": "forward", //此query的查詢順序,此處是forward,如果用了.sort({w:-1})將顯示backward。 "indexBounds": { //winningplan所掃描的索引範圍,此處查詢條件是w:1,使用的index是w與n的聯合索引,故w是[1.0,1.0]而n沒有指定在查詢條件中,故是[MinKey,MaxKey]。 "w": ["[1.0, 1.0]"], "n": ["[MinKey, MaxKey]"] } } }, "rejectedPlans": [{ //他執行計畫(非最優而被查詢最佳化工具reject的)的詳細返回,其中具體資訊與winningPlan的返回中意義相同 "stage": "FETCH", "inputStage": { "stage": "IXSCAN", "keyPattern": { "w": 1, "v": 1 }, "indexName": "w_1_v_1", "isMultiKey": false, "direction": "forward", "indexBounds": { "w": ["[1.0, 1.0]"], "v": ["[MinKey, MaxKey]"] } } }] }
indexFilterSet
IndexFilter決定了查詢最佳化工具對於某一類型的查詢將如何使用index,indexFilter僅影響查詢最佳化工具對於該類查詢可以用嘗試哪些index的執行計畫分析,查詢最佳化工具還是根據分析情況選擇最優計劃。
如果某一類型的查詢設定了IndexFilter,那麼執行時通過hint指定了其他的index,查詢最佳化工具將會忽略hint所設定index,仍然使用indexfilter中設定的查詢計劃。
IndexFilter可以通過命令移除,也將在執行個體重啟後清空。 IndexFilter的建立
db.runCommand( { planCacheSetFilter: <collection>, query: <query>, sort: <sort>, projection: <projection>, indexes: [ <index1>, <index2>, ...] })
db.runCommand( { planCacheSetFilter: "orders", query: { status: "A" }, indexes: [ { cust_id: 1, status: 1 }, { status: 1, order_date: -1 } ] })
針對orders表建立了一個indexFilter,indexFilter指定了對於orders表只有status條件(僅對status進行查詢,無sort等)的查詢的indexes,所以以下的查詢語句的查詢最佳化工具僅僅會從{cust_id:1,status:1}和{status:1,order_date:-1}中進行winning plan的選擇
db.orders.find( { status: "D" } )db.orders.find( { status: "P" } )
indexFilter的列表
可以通過如下命令展示某一個collecton的所有indexFilter
db.runCommand( { planCacheListFilters: <collection> } )
indexFilter的刪除
可以通過如下命令對IndexFilter進行刪除
db.runCommand( { planCacheClearFilters: <collection>, query: <query pattern>, sort: <sort specification>, projection: <projection specification> })
Stage返回參數說明
COLLSCAN #全表掃描IXSCAN #索引掃描FETCH #根據索引去檢索指定documentSHARD_MERGE #將各個分區返回資料進行mergeSORT #表明在記憶體中進行了排序(與老版本的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的返回
executionStats
該模式是mongoDB查詢的執行狀態,類似老版本的explain
"executionStats": { "executionSuccess": true, //是否執行成功 "nReturned": 29861, //查詢的返回條數 "executionTimeMillis": 23079, //整體執行時間 毫秒 "totalKeysExamined": 29861, // 索引掃描次數 "totalDocsExamined": 29861, // document掃描次數 "executionStages": { "stage": "FETCH", //這裡是FETCH去掃描對於documents "nReturned": 29861, //由於是FETCH,所以這裡該值與executionStats.nReturned一致 "executionTimeMillisEstimate": 22685, "works": 29862, //查看源碼中發現,每次操作會加1,且會把執行時間記錄在executionTimeMillis中。 "advanced": 29861,//而在查詢結束EOF,works又會加1,advanced不加。正常的返回works會比nReturned多1,這時候isEOF為true(1):另外advanced的傳回值只有在命中的時候+1,在skip,eof的時候不會增加 "needTime": 0, "needFetch": 0, "saveState": 946, "restoreState": 946, "isEOF": 1, "invalidates": 0, "docsExamined": 29861, // 與executionStats.totalDocsExamined一致 "alreadyHasObj": 0, "inputStage": { "stage": "IXSCAN", "nReturned": 29861, "executionTimeMillisEstimate": 70, "works": 29862, "advanced": 29861, "needTime": 0, "needFetch": 0, "saveState": 946, "restoreState": 946, "isEOF": 1, "invalidates": 0, "keyPattern": { "w": 1, "n": 1