MongoDB學習報告(二)

概述


MongoDB索引管理
MongoDB查詢優(yōu)化

MongoDB索引管理

  • 單鍵索引中的每一項都應該對應被索引文檔里面的一個值
  • 復合索引就是每一項都由多個鍵組合而成的索引握侧。復合索引里的鍵的順序是很重要攒盈。
  • 每一個索引都會對維護帶來成本牙勘,如果一個集合上有10個索引,每次寫操作都要更新10個索引,所以要保證沒有多余的索引
  • 使用索引起碼要保證索引都放到內存中,理想狀態(tài)是索引和使用中的集合都放到內存里面肃续。
  • <b style=color:red>一個集合可以創(chuàng)建多個索引,但是只會使用最合適的一個叉袍,$or例外(待明確)</b>

索引管理

<b style=color:#87CEFA>創(chuàng)建索引:</b>索引的創(chuàng)建方法3.0之前使用ensureInde()方法始锚,3.0之后使用的是createIndex(),1為指定按升序創(chuàng)建索引喳逛,如果你想按降序來創(chuàng)建索引指定為-1瞧捌。如果文檔中嵌套文檔,可以建立子文檔的索引润文。對應大數(shù)據(jù)集合姐呐,構建索引可能會需要幾個小時或者幾天,background項允許索引創(chuàng)建在后臺執(zhí)行典蝌。

#單鍵索引曙砂,用戶表為例。
db.user.createIndex({name: 1});
#復合索引
db.user.createIndex({name: 1,age: -1});
#輸出顯示骏掀,索引創(chuàng)建成功
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}
#子文檔索引鸠澈,假如用戶的地址address是一個文檔,可以以address的city值建立索引
db.user.createIndex({address.city: 1});
#后臺執(zhí)行索引創(chuàng)建
db.user.createIndex({name: 1,age: -1},{background: true});

<b style=color:#87CEFA>查詢索引:</b>

 db.user.getIndexes();
#結果
[
    {
        "v" : 1,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "test.user"
    },
    {
        "v" : 1,
        "unique" : true,
        "key" : {
            "name" : 1
        },
        "name" : "name_1",
        "ns" : "test.user"
    }
]

<b style=color:#87CEFA>刪除索引:</b>dropIndex()刪除指定索引截驮,dropIndexes()刪除全部索引笑陈,_id除外。

#刪除索引名為name_1的索引
db.user.dropIndex("name_1");
#刪除所有索引
db.user.dropIndexes();

<b style=color:#87CEFA>唯一索引:</b>創(chuàng)建方式葵袭,設置unique涵妥。唯一索引通常插入數(shù)據(jù)之前創(chuàng)建,提前創(chuàng)建避免數(shù)據(jù)重復導致唯一索引建立失敗坡锡,如果數(shù)據(jù)不重要妹笆,可以使用dropDups選項告訴數(shù)據(jù)庫自動刪除重復文檔块请。

#設置name鍵唯一索引,
db.user.createIndex({name: 1},{unique: true});
#設置唯一索引后插入重復數(shù)據(jù)會有一個錯誤提示
WriteResult({
    "nInserted" : 0,
    "writeError" : {
        "code" : 11000,
        "errmsg" : "E11000 duplicate key error collection: test.user index: name_1 dup key: { : \"lance\" }"
    }
})
#已有重復數(shù)據(jù)的集合創(chuàng)建索引
db.user.createIndex({name: 1},{unique: true,dropDups: true});

<b style=color:#87CEFA>稀疏索引:</b>索引默認都密集型的,就是說一個有索引的集合中拳缠,每一個文檔都有對應的索引項,如果文檔沒有索引鍵贸弥,對應的索引也會有個null的值窟坐。例如上面的用戶表中,有的用戶沒有age的鍵绵疲,假如創(chuàng)建一個age的索引哲鸳,* db.user.find({age: null}) *查詢也是可以使用到索引定位到對應的用戶。但是有些情況下密集型索引并不適合盔憨,例如:

  • 商城的產(chǎn)品集合都有個sku的唯一碼徙菠,如果sku鍵有唯一索引,現(xiàn)在要插入多個沒有sku的產(chǎn)品郁岩,那么第一次會成功婿奔,之后的都會失敗,因為唯一索引里sku已經(jīng)有了null的項问慎。這種情況下就需要稀疏索引萍摊。
db.product.createIndex({sku: 1},{unique: true, sparse: true})
  • 評論功能user_id鍵建立索引,但是允許匿名評論如叼。這種情況下如果使用密集型冰木,因為集合中大量的文檔user_id的鍵不存在,索引就會擁有大量的null的項都會是null笼恰,這樣會增加了索引的大小踊沸,更重要的是user_id為null的文檔寫操作的時候也會更新索引。極少或者不對匿名用戶進行查詢的情況下應該使用稀疏索引社证。
db.commons.createIndex({user_id}: 1},{sparse: true})

<b style=color:#87CEFA>多鍵索引:</b>MongoDB允許索引中的多個條目指向同一個文檔逼龟,多鍵索引默認處于激活狀態(tài),只要被索引鍵為數(shù)組每個數(shù)組值都會在索引中有自己的位置猴仑。例如文章集合中每篇文章可能有多個標簽审轮,用數(shù)組tags記錄。假如在tags鍵上建立索引辽俗,則tags每個數(shù)組值都會在索引中疾渣。

{
    "title":文章列表,
    "tags":["mysql", "nosql", "nginx"]    
}

MongoDB查詢優(yōu)化

<b style=color:#87CEFA>慢查詢:</b>開啟可以有效的識別語句消耗是否超過預期。Mongodb開啟慢查詢使用的是Profiling工具崖飘,該工具在運行的實例上收集有關MongoDB的寫操作榴捡,游標,數(shù)據(jù)庫命令等朱浴,可以在數(shù)據(jù)庫級別開啟該工具吊圾,也可以在實例級別開啟达椰。該工具會把收集到的所有都寫入到system.profile<b style=color:red>固定集合</b>,集合大小是固定的,超過最大默認值時新記錄覆蓋舊記錄项乒。

  • 可以修改配置文件默認開啟慢查詢
#打開配置文件啰劲,添加慢查詢配置。profile是開啟的級別檀何,0是關閉蝇裤;1是收集慢查詢數(shù)據(jù),開啟慢查詢频鉴;2是記錄所有的操作栓辜。slowms慢查詢時間,單位毫秒垛孔,默認100ms
profile=1
slowms=200
  • 直接在對應的數(shù)據(jù)庫執(zhí)行開啟命令
#開啟慢查詢藕甩,記錄執(zhí)行時間超過200ms的查詢語句。如果不填則默認100ms周荐。
db.setProfilingLeve(1,200);
#輸出效果
{ "was" : 0, "slowms" : 200, "ok" : 1 } 
#可以查詢當前數(shù)據(jù)開啟的profile
db.getProfilingLeve();
#查詢用戶表
db.user.find();
#查看慢system.profile集合,pretty格式化輸出
db.system.profile.find().pretty()
{
    "op" : "query", #操作類型,有insert羡藐、query贩毕、update、remove仆嗦、getmore辉阶、command   
    "ns" : "test.user", #操作的集合
    "query" : {  #查詢語句
        "find" : "user",
        "filter" : {
            
        }
    },
    "keysExamined" : 0,#索引掃描條目,對應3.2版本之前的nscanned
    "docsExamined" : 2, #文檔掃描條目瘩扼,對應3.2版本之前的nscannedObjects
    "cursorExhausted" : true,
    "keyUpdates" : 0,#在操作里更新的改變 index 的數(shù)量谆甜。改變一個索引值會消耗一點性能,因為數(shù)據(jù)庫必須移掉老的值并插入新的值到B-tree索引中集绰。
    "writeConflicts" : 0,
    "numYield" : 0,
    "locks" : {  #鎖信息规辱,R:全局讀鎖;W:全局寫鎖栽燕;r:特定數(shù)據(jù)庫的讀鎖罕袋;w:特定數(shù)據(jù)庫的寫鎖
        ......
    },
    "nreturned" : 2,  #返回的記錄數(shù)。如果用limit(n)命令將返回n個文檔碍岔,ntoreturn值是n
    "responseLength" : 314,  #結果字節(jié)長度浴讯,一個大的 responseLength會影響性能.
    "protocol" : "op_command",
    "millis" : 106,    #消耗的時間(毫秒)
    "execStats" : {
        ......
    },
    "ts" : ISODate("2016-07-22T07:32:46.289Z"),#語句執(zhí)行的時間
    "client" : "127.0.0.1",#鏈接ip或則主機
    "allUsers" : [
        {
            "user" : "lance",
            "db" : "admin"
        }
    ],
    "user" : "lance@admin" #用戶
}

#執(zhí)行帶索引的搜索,"millis" : 13蔼啦,keysExamined和docsExamined都變成1榆纽。
db.user.find({name:"lance"})

<b style=color:#87CEFA>分析慢查詢:</b>
** 使用并了解EXPLAIN() **

  • 3.0版本之前,可以直接用explain()函數(shù)
  • 3.0版本后explain有所改動,分成三種模式奈籽,默認queryPlanner饥侵,executionStats,allPlansExecution
    • queryPlanner查詢計劃的選擇器衣屏,首先進行查詢分析躏升,最終選擇一個winningPlan,是explain返回的默認層面勾拉。
    • executionStats為執(zhí)行統(tǒng)計層面煮甥,返回winningPlan的統(tǒng)計結果
    • allPlansExecution為返回所有執(zhí)行計劃的統(tǒng)計,包括rejectedPlan
db.test.find({name:'lance'}).explain()
#結果
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.test", #該值返回的是該query所查詢的表
        "indexFilterSet" : false, #該query是否有indexfilter
        "parsedQuery" : {
            "name" : {
                "$eq" : "lance"
            }
        },
        "winningPlan" : {  #查詢優(yōu)化器針對該query所返回的最優(yōu)執(zhí)行計劃的詳細內容
            "stage" : "FETCH",#最優(yōu)執(zhí)行計劃的stage藕赞,這里返回是FETCH,可以理解為通過返回的index位置去檢索具體的文檔(下面有不同類型的介紹)
            "inputStage" : { #用來描述子stage卖局,并且為其父stage提供文檔和索引關鍵字斧蜕。
                "stage" : "IXSCAN", 
                "keyPattern" : {
                    "name" : 1  #所掃描的index內容
                },
                "indexName" : "name_1",
                "isMultiKey" : false, #是否是Multikey,此處返回是false砚偶,如果索引建立在array上批销,此處將是true
                ......
                "direction" : "forward", #此query的查詢順序,此處是forward染坯,如果用了.sort({modify_time:-1})將顯示backward
                "indexBounds" : { #所掃描的索引范圍,如果沒有制定范圍就是[MaxKey, MinKey]均芽,這主要是直接定位到mongodb的chunck中去查找數(shù)據(jù),加快數(shù)據(jù)讀取单鹿。
                    "name" : [
                        "[\"lance\", \"lance\"]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ] #其他執(zhí)行計劃(非最優(yōu)而被查詢優(yōu)化器reject的)的詳細返回
    },
    ......
}

db.test.find({name:'lance'}).explain('executionStats')
{
    "queryPlanner" : {
        ...... 
    },
    "executionStats" : {
        "executionSuccess" : true, #是否執(zhí)行成功
        "nReturned" : 1, #滿足查詢條件的文檔個數(shù)掀宋,即查詢的返回條數(shù)
        "executionTimeMillis" : 0, #整體執(zhí)行時間
        "totalKeysExamined" : 1, #索引整體掃描的文檔個數(shù)
        "totalDocsExamined" : 1,#document掃描個數(shù)
        "executionStages" : {
            ......
        }
    },
    ......
}

** stage的類型的意義 **

  • COLLSCAN :全表掃描
  • IXSCAN:索引掃描
  • FETCH::根據(jù)索引去檢索指定document
  • SHARD_MERGE:各個分片返回數(shù)據(jù)進行merge
  • SORT:表明在內存中進行了排序(與前期版本的scanAndOrder:true一致)
  • SORT_MERGE:表明在內存中進行了排序后再合并
  • LIMIT:使用limit限制返回數(shù)
  • SKIP:使用skip進行跳過
  • IDHACK:針對_id進行查詢
  • SHARDING_FILTER:通過mongos對分片數(shù)據(jù)進行查詢
  • COUNT:利用db.coll.count()之類進行count運算
  • COUNTSCAN:count不使用用Index進行count時的stage返回
  • COUNT_SCAN:count使用了Index進行count時的stage返回
  • SUBPLA:未使用到索引的$or查詢的stage返回
  • TEXT:使用全文索引進行查詢時候的stage返回
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市仲锄,隨后出現(xiàn)的幾起案子劲妙,更是在濱河造成了極大的恐慌,老刑警劉巖儒喊,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镣奋,死亡現(xiàn)場離奇詭異,居然都是意外死亡怀愧,警方通過查閱死者的電腦和手機侨颈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芯义,“玉大人哈垢,你說我怎么就攤上這事”显簦” “怎么了温赔?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鬼癣。 經(jīng)常有香客問我陶贼,道長啤贩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任拜秧,我火速辦了婚禮痹屹,結果婚禮上,老公的妹妹穿的比我還像新娘枉氮。我一直安慰自己志衍,他們只是感情好,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布聊替。 她就那樣靜靜地躺著楼肪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惹悄。 梳的紋絲不亂的頭發(fā)上春叫,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音泣港,去河邊找鬼暂殖。 笑死,一個胖子當著我的面吹牛当纱,可吹牛的內容都是我干的呛每。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼坡氯,長吁一口氣:“原來是場噩夢啊……” “哼晨横!你這毒婦竟也來了?” 一聲冷哼從身側響起廉沮,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤颓遏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后滞时,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叁幢,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年坪稽,在試婚紗的時候發(fā)現(xiàn)自己被綠了曼玩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡窒百,死狀恐怖黍判,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情篙梢,我是刑警寧澤顷帖,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響贬墩,放射性物質發(fā)生泄漏榴嗅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一陶舞、第九天 我趴在偏房一處隱蔽的房頂上張望嗽测。 院中可真熱鬧,春花似錦肿孵、人聲如沸唠粥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽晤愧。三九已至,卻和暖如春蛉腌,著一層夾襖步出監(jiān)牢的瞬間养涮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工眉抬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人懈凹。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓蜀变,卻偏偏與公主長得像,于是被迫代替她去往敵國和親介评。 傳聞我的和親對象是個殘疾皇子库北,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內容