mongo 索引解析

方丈發表於2019-04-30

摘要

mongo 的索引非常強大,和關係型資料庫索引沒什麼區別。這裡主要介紹mongo索引基本知識和mongo本人在索引上的犯的錯。

索引種類

  1. 單欄位索引

  2. 複合索引 複合索引各個欄位的順序應該是精確匹配欄位(=xxx),排序欄位(避免在記憶體中排序,使用index排序),範圍查詢欄位

    如db.book.find({company: 'xxx', age:{$lt:30}).sort({name:1}) db.book.find().explain("executionStats")可以很好的列出查詢執行計劃。 總共有四個重要引數: executionTimeMills:查詢執行的時間 nReturned: 返回的文件數 totalKeysExamined: 索引掃描數 totalDocsExamined: 文件掃描數

    當然希望nReturned數目=totalKeysExamined 不掃描文件。(後面不掛著資料,index及資料)

    或者nReturned = totalKeysExamined = totalDocsExamined 如果有排序,為了不讓排序在記憶體中進入,在nReturned = totalDocsExamined的基礎上,totalKeysExamined可以大於nReturned。對於大資料量的記憶體排序會非常消耗效能

    如果我們建立一個複合索引是db.book.ensureIndex({company:1,age:1,name:1}) 這時候nReturned = totalKeysExamined = totalDocsExamined 。因為查詢會用到index,不需要額外的文件掃描。但是會有SORT stage,即在記憶體中排序,在大資料量的情況下記憶體排序是很慢的。

    嘗試加一個index,在排序欄位放在掃描欄位前面 db.book.ensureIndex({company:1,name:1,age:1}) 這時候發現mongo選擇了新的index

     "indexBounds" : {
             "company" : [
                     "[\"a\", \"a\"]"
             ],
             "name" : [
                     "[MinKey, MaxKey]"
             ],
             "age" : [
                     "[-1.#INF, 30.0)"
             ]
     },
    複製程式碼

    且執行計劃中有reject SORT排序

     "rejectedPlans" : [
             {
                     "stage" : "SORT",
                     "sortPattern" : {
                             "name" : 1
                     },
    複製程式碼

    這時候nReturned = totalDocsExamined < totalKeysExamined 多掃描了index,但是是值得的。這也是為什麼在開始的時候時候說聯合index的欄位排序順序是精確匹配欄位(=xxx),排序欄位(避免在記憶體中排序,使用index排序),範圍查詢欄位 如{name:1,address:1},包含的是兩個查詢

    db.book.find({name:"xxx"})
    db.book.find({name:"xxx",address:"xxx"})
    複製程式碼

    但是如果你的查詢不是範圍查詢。而是精確匹配欄位。那還是使用原來的index。因為這時候排序欄位用到了index查詢,不需要SORT階段了

     db.book.find({company:'a',age:30}).sort({name:1}).explain("executionStats")
      "indexBounds" : {
              "company" : [
                      "[\"a\", \"a\"]"
              ],
              "age" : [
                      "[30.0, 30.0]"
              ],
              "name" : [
                      "[MinKey, MaxKey]"
              ]
      },
    複製程式碼
  3. 多鍵索引 如array索引 docs.mongodb.com/manual/core…

  • 多鍵索引是沒法查一個陣列全部匹配的,會先查第一個元素,後面的會使用filter

  • $elemMatch

    son:{ $elemMatch:{$gt:9,$lt:11}} 這個查詢和son:{$gt:9,$lt:11}的區別, 後者是隻要陣列中任意一個欄位滿足其他一個條件即可,比如第一個欄位滿足gt:9,第二個欄位滿足lt:11那麼也認為是滿足條件。所以使用索引時,只能使用到一個邊界條件。

  • 在聯合索引中只允許有一個array欄位。但是因為mongo是free schema的。可以是不同的欄位,只要一個document中只有一個array就行了,在不同的document中可以是不同欄位

  1. 唯一索引 db.book.createIndex({"name":1},{"unique":true}) mongo 預設建立的不是唯一索引,需要顯示指定。唯一索引會對資料進行校驗,不允許重複資料。

  2. sharding cluster 索引 索引是在各個shard上面單獨建立的,不是全域性的。 sharding cluster 環境,只允許_id,和shard key建立unique index.因為unique index 需要shard 之間通訊,違背了shard 設計理念。所以需要避免

注意

  1. 當一個collection上面有多個index 某個查詢可能命中多個index,這時候mongo是如何選擇索引的呢。

    首先mongo會對某類類似查詢語句在可能命中的index都執行一遍,並行執行的,最早返回100個結果找出最優的index,然後記住這類查詢所用到的索引。以後查詢操作就使用這個索引。當有index更改時,再去更改這個值。
    複製程式碼
  2. 當有一個複合索引 {name:1,address:1,email:1}

    這時候有一個新的查詢{name:xxx,address:xxx,phone:xxx} 可以用到已經建立的複合索引。這時候你會不會單獨在建立一個索引呢。 優勢是這個查詢也很快,缺點是多了一個index,減弱了插入效能。

    這個可能需要衡量前兩個欄位過濾掉了多少資料,phone這個欄位佔剩下資料量的多少來決定需要建立什麼樣的index.

  3. mongo 中有一個名字叫scalar(標量欄位)就是非array,非embedded document這樣的欄位。針對這些欄位的索引與關係型資料庫並無差別,無需特殊處理 覺得這篇分享就有點過於強調閱讀mongo原始碼來解決的問題的重要性,因為這個就可以通過上述分析找到root cause yq.aliyun.com/articles/74… #array index# mongo 可以對array建立index,注意是將index中的每個元素都作為index key,進行索引。所以對array建立index一定要十分小心,很容易導致index size 很大。另外mongo支援指定array某一列進行查詢。

test.book
{
	_id:1,
	name:english,
	address:[addr1,addr2]
}
複製程式碼

db.book.find({"address.0":"addr1"}) 當對address建立index,這樣的查詢是用不到index的。只有基於array的查詢,index才能有效。 mongo並沒有那麼神奇的在建立index的同時還保留列數。

#shard key index#

  • 表中有資料 表中有資料再建立shard key,需要首先建立對應的index,才能去建立shard key
  • 表中無資料 表中無資料,建立shard key的同時,mongo會自動建立一個對應欄位的index
sh.shardCollection("test.book",{name:1,address:1})
複製程式碼

會自動建立index

{name:1,address:1}
複製程式碼

mongo index VS cassandra secondary index

1.query 過程 cassandra query,首先根據partitioner key去找對應partition,partition中的資料是按照clustering key排序的。注意是按照clustering key排序的,clustering key這個欄位 不是index。

mongo(sharding cluster) query,首先根據給定的shard key去找在哪個節點上,然後將請求傳送到此節點。進行查詢。 如果你的query case是

db.book.find({name:"xxx",address:"xxx"})
複製程式碼

而shard key是name。此外再單獨為address建立一個index。這時候你的query其實是命中的address 的單欄位index。而不是預想的已經將name資料過濾了。這點和cassandra有很大的不同

2.範圍 cassandra secondary index 是local的,在每個節點上。 mongo 的index是全域性的。 mongo sharding cluster 環境,index也是在各個shard上獨立建立的。

參考

www.mongoing.com/eshu_explai…

相關文章