国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

MongoDB指南---11、使用復合索引、$操作符如何使用索引、索引對象和數組、索引基數

tomlingtm / 756人閱讀

摘要:操作符如何使用索引有一些查詢完全無法使用索引,也有一些查詢能夠比其他查詢更高效地使用索引。有時能夠使用索引,但是通常它并不知道要如何使用索引。索引對象和數組允許深入文檔內部,對嵌套字段和數組建立索引。

上一篇文章:MongoDB指南---10、索引、復合索引 簡介
下一篇文章:MongoDB指南---12、使用explain()和hint()、何時不應該使用索引
1、使用復合索引

在多個鍵上建立的索引就是復合索引,在上面的小節中,已經使用過復合索引。復合索引比單鍵索引要復雜一些,但是也更強大。本節會更深入地介紹復合索引。

1. 選擇鍵的方向

到目前為止,我們的所有索引都是升序的(或者是從最小到最大)。但是,如果需要在兩個(或者更多)查詢條件上進行排序,可能需要讓索引鍵的方向不同。例如,假設我們要根據年齡從小到大,用戶名從Z到A對上面的集合進行排序。對于這個問題,之前的索引變得不再高效:每一個年齡分組內都是按照"username"升序排列的,是A到Z,不是Z到A。對于按"age"升序排列按"username"降序排列這樣的需求來說,用上面的索引得到的數據的順序沒什么用。
為了在不同方向上優化這個復合排序,需要使用與方向相匹配的索引。在這個例子中,可以使用{"age" : 1, "username" : -1},它會以下面的方式組織數據:

[21, "user999977"] -> 0xe57bf737
[21, "user999954"] -> 0x8bffa512
[21, "user999902"] -> 0x9e1447d1
[21, "user999900"] -> 0x3a6a8426
[21, "user999874"] -> 0xc353ee06
...
[30, "user999936"] -> 0x7f39a81a
[30, "user999850"] -> 0xa979e136
[30, "user999775"] -> 0x5de6b77a
...
[30, "user100324"] -> 0xe14f8e4d
[30, "user100140"] -> 0x0f34d446
[30, "user100050"] -> 0x223c35b1

年齡按照從年輕到年長順序排列,在每一個年齡分組中,用戶名是從Z到A排列的(對于我們的用戶名來說,也可以說是按照"9"到"0"排列的)。
如果應用程序同時需要按照{"age" : 1, "username" : 1}優化排序,我們還需要創建一個這個方向上的索引。至于索引使用的方向,與排序方向相同就可以了。注意,相互反轉(在每個方向都乘以-1)的索引是等價的:{"age" : 1, "user name" : -1}適用的查詢與{"age" : -1, "username" : 1}是完全一樣的。
只有基于多個查詢條件進行排序時,索引方向才是比較重要的。如果只是基于單一鍵進行排序,MongoDB可以簡單地從相反方向讀取索引。例如,如果有一個基于{"age" : -1}的排序和一個基于{"age" : 1}的索引,MongoDB會在使用索引時進行優化,就如同存在一個{"age" : -1}索引一樣(所以不要創建兩個這樣的索引!)。只有在基于多鍵排序時,方向才變得重要。

2. 使用覆蓋索引(covered index)

在上面的例子中,查詢只是用來查找正確的文檔,然后按照指示獲取實際的文檔。然后,如果你的查詢只需要查找索引中包含的字段,那就根本沒必要獲取實際的文檔。當一個索引包含用戶請求的所有字段,可以認為這個索引覆蓋了本次查詢。在實際中,應該優先使用覆蓋索引,而不是去獲取實際的文檔。這樣可以保證工作集比較小,尤其與右平衡索引一起使用時。
為了確保查詢只使用索引就可以完成,應該使用投射(詳見4.1.1節)來指定不要返回"_id"字段(除非它是索引的一部分)。可能還需要對不需要查詢的字段做索引,因此需要在編寫時就在所需的查詢速度和這種方式帶來的開銷之間做好權衡。
如果在覆蓋索引上執行explain(),"indexOnly"字段的值要為true。
如果在一個含有數組的字段上做索引,這個索引永遠也無法覆蓋查詢(因為數組是被保存在索引中的,5.1.4節會深入介紹)。即便將數組字段從需要返回的字段中剔除,這樣的索引仍然無法覆蓋查詢。

3. 隱式索引

復合索引具有雙重功能,而且對不同的查詢可以表現為不同的索引。如果有一個{"age" : 1, "username" : 1}索引,"age"字段會被自動排序,就好像有一個{"age" : 1}索引一樣。因此,這個復合索引可以當作{"age" : 1}索引一樣使用。
這個可以根據需要推廣到盡可能多的鍵:如果有一個擁有N個鍵的索引,那么你同時“免費”得到了所有這N個鍵的前綴組成的索引。舉例來說,如果有一個{"a": 1, "b": 1, "c": 1, ..., "z": 1}索引,那么,實際上我們也可以使用 {"a": 1}、{"a": 1, "b" : 1}、{"a": 1, "b": 1, "c": 1}等一系列索引。
注意,這些鍵的任意子集所組成的索引并不一定可用。例如,使用{"b": 1}或者{"a": 1, "c": 1}作為索引的查詢是不會被優化的:只有能夠使用索引前綴的查詢才能從中受益。

2、$操作符如何使用索引

有一些查詢完全無法使用索引,也有一些查詢能夠比其他查詢更高效地使用索引。本節講述MongoDB對各種不同查詢操作符的處理。

1. 低效率的操作符

有一些查詢完全無法使用索引,比如"$where"查詢和檢查一個鍵是否存在的查詢({"key" : {"$exists" : true}})。也有其他一些操作不能高效地使用索引。
如果"x"上有一個索引,查詢那些不包含"x"鍵的文檔可以使用這樣的索引({"x" : {"$exists" : false}}。然而,在索引中,不存在的字段和null字段的存儲方式是一樣的,查詢必須遍歷每一個文檔檢查這個值是否真的為null還是根本不存在。如果使用稀疏索引(sparse index),就不能使用{"$exists" : true},也不能使用{"$exists" : false}。
通常來說,取反的效率是比較低的。"$ne"查詢可以使用索引,但并不是很有效。因為必須要查看所有的索引條目,而不只是"$ne"指定的條目,不得不掃描整個索引。例如,這樣的查詢遍歷的索引范圍如下:

> db.example.find({"i" : {"$ne" : 3}}).explain()
{
    "cursor" : "BtreeCursor i_1 multi",
    ...,
    "indexBounds" : {
        "i" : [
            [
                {
                    "$minElement" : 1
                },
                3
            ],
            [
                3,
                {
                    "$maxElement" : 1
                }
            ]
        ]
    },
    ...
}

這個查詢查找了所有小于3和大于3的索引條目。如果索引中值為3的條目非常多,那么這個查詢的效率是很不錯的,否則的話,這個查詢就不得不檢查幾乎所有的索引條目。
"$not"有時能夠使用索引,但是通常它并不知道要如何使用索引。它能夠對基本的范圍(比如將{"key" : {"$lt" : 7}} 變成 {"key" : {"$gte" : 7}})和正則表達式進行反轉。然而,大多數使用"$not"的查詢都會退化為進行全表掃描。"$nin"就總是進行全表掃描。
如果需要快速執行一個這些類型的查詢,可以試著找到另一個能夠使用索引的語句,將其添加到查詢中,這樣就可以在MongoDB進行無索引匹配(non-indexed matching)時先將結果集的文檔數量減到一個比較小的水平。
假如我們要找出所有沒有"birthday"字段的用戶。如果我們知道從3月20開始,程序會為每一個新用戶添加生日字段,那么就可以只查詢3月20之前創建的用戶:

> db.users.find({"birthday" : {"$exists" : false}, "_id" : {"$lt" : march20Id}})

這個查詢中的字段順序無關緊要,MongoDB會自動找出可以使用索引的字段,而無視查詢中的字段順序。

2. 范圍

復合索引使MongoDB能夠高效地執行擁有多個語句的查詢。設計基于多個字段的索引時,應該將會用于精確匹配的字段(比如 "x" : "foo")放在索引的前面,將用于范圍匹配的字段(比如"y" : {"$gt" : 3, "$lt" : 5})放在最后。這樣,查詢就可以先使用第一個索引鍵進行精確匹配,然后再使用第二個索引范圍在這個結果集內部進行搜索。假設要使用{"age" : 1, "username" : 1}索引查詢特定年齡和用戶名范圍內的文檔,可以精確指定索引邊界值:

> db.users.find({"age" : 47,
... "username" : {"$gt" : "user5", "$lt" : "user8"}}).explain()
{
    "cursor" : "BtreeCursor age_1_username_1",
    "n" : 2788,
    "nscanned" : 2788,
    ...,
    "indexBounds" : {
        "age" : [
            [
                47,
                47
            ]
        ],
        "username" : [
            [
                "user5",
                "user8"
            ]
        ]
    },
    ...
}

這個查詢會直接定位到"age"為47的索引條目,然后在其中搜索用戶名介于"user5"和"user8"的條目。
反過來,假如使用{"username" : 1, "age" : 1}索引,這樣就改變了查詢計劃(query plan),查詢必須先找到介于"user5"和"user8"之間的所有用戶,然后再從中挑選"age"等于47的用戶。

> db.users.find({"age" : 47,
... "username" : {"$gt" : "user5", "$lt" : "user8"}}).explain()
{
    "cursor" : "BtreeCursor username_1_age_1",
    "n" : 2788,
    "nscanned" : 319499,
    ...,
    "indexBounds" : {
        "username" : [
            [
                "user5",
                "user8"
            ]
        ],
        "age" : [
            [
                47,
                47
            ]
        ]
    },
    "server" : "spock:27017"
}

本次查詢中MongoDB掃描的索引條目數量是前一個查詢的10倍!在一次查詢中使用兩個范圍通常會導致低效的查詢計劃。

3. OR查詢

寫作本書時,MongoDB在一次查詢中只能使用一個索引。如果你在{"x" : 1}上有一個索引,在{"y" : 1}上也有一個索引,在{"x" : 123, "y" : 456}上進行查詢時,MongoDB會使用其中的一個索引,而不是兩個一起用。"$or"是個例外,"$or"可以對每個子句都使用索引,因為"$or"實際上是執行兩次查詢然后將結果集合并。

 > db.foo.find({"$or" : [{"x" : 123}, {"y" : 456}]}).explain()
 {
      "clauses" : [
        {
            "cursor" : "BtreeCursor x_1",
            "isMultiKey" : false,
            "n" : 1,
            "nscannedObjects" : 1,
            "nscanned" : 1,
            "nscannedObjectsAllPlans" : 1,
            "nscannedAllPlans" : 1,
            "scanAndOrder" : false,
            "indexOnly" : false,
            "nYields" : 0,
            "nChunkSkips" : 0,
            "millis" : 0,
            "indexBounds" : {
                "x" : [
                    [
                        123,
                        123
                    ]
            ]
        }
    },
    {
        "cursor" : "BtreeCursor y_1",
        "isMultiKey" : false,
        "n" : 1,
        "nscannedObjects" : 1,
        "nscanned" : 1,
        "nscannedObjectsAllPlans" : 1,
        "nscannedAllPlans" : 1,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 0,
        "indexBounds" : {
            "y" : [
                    [
                        456,
                        456
                    ]
                ]
            }
        }
    ],
    "n" : 2,
    "nscannedObjects" : 2,
    "nscanned" : 2,
    "nscannedObjectsAllPlans" : 2,
    "nscannedAllPlans" : 2,
    "millis" : 0,
    "server" : "spock:27017"
}

可以看到,這次的explain()輸出結果由兩次獨立的查詢組成。通常來說,執行兩次查詢再將結果合并的效率不如單次查詢高,因此,應該盡可能使用"$in"而不是"$or"。
如果不得不使用"$or",記住,MongoDB需要檢查每次查詢的結果集并且從中移除重復的文檔(有些文檔可能會被多個"$or"子句匹配到)。
使用"$in"查詢時無法控制返回文檔的順序(除非進行排序)。例如,使用{"x" : [1, 2, 3]}與使用{"x" : [3, 2, 1]}得到的文檔順序是相同的。

 3、索引對象和數組

MongoDB允許深入文檔內部,對嵌套字段和數組建立索引。嵌套對象和數組字段可以與復合索引中的頂級字段一起使用,雖然它們比較特殊,但是大多數情況下與“正常”索引字段的行為是一致的。

1. 索引嵌套文檔

可以在嵌套文檔的鍵上建立索引,方式與正常的鍵一樣。如果有這樣一個集合,其中的第一個文檔表示一個用戶,可能需要使用嵌套文檔來表示每個用戶的位置:

{
    "username" : "sid",
    "loc" : {
        "ip" : "1.2.3.4",
        "city" : "Springfield",
        "state" : "NY"
    }
}

需要在"loc"的某一個子字段(比如"loc.city")上建立索引,以便提高這個字段的查詢速度:

> db.users.ensureIndex({"loc.city" : 1})

可以用這種方式對任意深層次的字段建立索引,比如你可以在"x.y.z.w.a.b.c"上建立索引。
注意,對嵌套文檔本身("loc")建立索引,與對嵌套文檔的某個字段("loc.city")建立索引是不同的。對整個子文檔建立索引,只會提高整個子文檔的查詢速度。在上面的例子中,只有在進行與子文檔字段順序完全匹配的子文檔查詢時(比如db.users.find({"loc" : {"ip" : "123.456.789.000", "city" : "Shelbyville", "state" : "NY"}}})),查詢優化器才會使用"loc"上的索引。無法對形如db.users.find({"loc.city" : "Shelbyville"})的查詢使用索引。

2. 索引數組

也可以對數組建立索引,這樣就可以高效地搜索數組中的特定元素。
假如有一個博客文章的集合,其中每個文檔表示一篇文章。每篇文章都有一個"comments"字段,這是一個數組,其中每個元素都是一個評論子文檔。如果想要找出最近被評論次數最多的博客文章,可以在博客文章集合中嵌套的"comments"數組的"date"鍵上建立索引:

> db.blog.ensureIndex({"comments.date" : 1})

對數組建立索引,實際上是對數組的每一個元素建立一個索引條目,所以如果一篇文章有20條評論,那么它就擁有20個索引條目。因此數組索引的代價比單值索引高:對于單次插入、更新或者刪除,每一個數組條目可能都需要更新(可能有上千個索引條目)。
與上一節中"loc"的例子不同,無法將整個數組作為一個實體建立索引:對數組建立索引,實際上是對數組中的每個元素建立索引,而不是對數組本身建立索引。
在數組上建立的索引并不包含任何位置信息:無法使用數組索引查找特定位置的數組元素,比如"comments.4"。
少數特殊情況下,可以對某個特定的數組條目進行索引,比如:

> db.blog.ensureIndex({"comments.10.votes": 1})

然而,只有在精確匹配第11個數組元素時這個索引才有用(數組下標從0開始)。
一個索引中的數組字段最多只能有一個。這是為了避免在多鍵索引中索引條目爆炸性增長:每一對可能的元素都要被索引,這樣導致每個文檔擁有n*m個索引條目。假如有一個{"x" : 1, "y" : 1}上的索引:

> // x是一個數組—— 這是合法的
> db.multi.insert({"x" : [1, 2, 3], "y" : 1})
>
> // y是一個數組——這也是合法的
> db.multi.insert({"x" : 1, "y" : [4, 5, 6]})
>
> // x和y都是數組——這是非法的!
> db.multi.insert({"x" : [1, 2, 3], "y" : [4, 5, 6]})
cannot index parallel arrays [y] [x]

如果MongoDB要為上面的最后一個例子創建索引,它必須要創建這么多索引條目:{"x" : 1, "y" : 4}、{"x" : 1, "y" : 5}、{"x" : 1, "y" : 6}、{"x" : 2, "y" : 4}、{"x" : 2, "y" : 5},{"x" : 2, "y" : 6}、{"x" : 3, "y" : 4}、{"x" : 3, "y" : 5}和{"x" : 3, "y" : 6}。盡管這些數組只有3個元素。

3. 多鍵索引

對于某個索引的鍵,如果這個鍵在某個文檔中是一個數組,那么這個索引就會被標記為多鍵索引(multikey index)。可以從explain()的輸出中看到一個索引是否為多鍵索引:如果使用了多鍵索引,"isMultikey"字段的值會是true。索引只要被標記為多鍵索引,就無法再變成非多鍵索引了,即使這個字段為數組的所有文檔都從集合中刪除。要將多鍵索引恢復為非多鍵索引,唯一的方法就是刪除再重建這個索引。
多鍵索引可能會比非多鍵索引慢一些。可能會有多個索引條目指向同一個文檔,因此MongoDB在返回結果集時必須要先去除重復的內容。

4、索引基數

基數(cardinality)就是集合中某個字段擁有不同值的數量。有一些字段,比如"gender"或者"newsletter opt-out",可能只擁有兩個可能的值,這種鍵的基數就是非常低的。另外一些字段,比如"username"或者"email",可能集合中的每個文檔都擁有一個不同的值,這類鍵的基數是非常高的。當然也有一些介于兩者之間的字段,比如"age"或者"zip code"。
通常,一個字段的基數越高,這個鍵上的索引就越有用。這是因為索引能夠迅速將搜索范圍縮小到一個比較小的結果集。對于低基數的字段,索引通常無法排除掉大量可能的匹配。
假設我們在"gender"上有一個索引,需要查找名為Susan的女性用戶。通過這個索引,只能將搜索空間縮小到大約50%,然后要在每個多帶帶的文檔中查找"name"為"Susan"的用戶。反過來,如果在"name"上建立索引,就能立即將結果集縮小到名為"Susan"的用戶,這樣的結果集非常小,然后就可以根據性別從中迅速地找到匹配的文檔了。
一般說來,應該在基數比較高的鍵上建立索引,或者至少應該把基數較高的鍵放在復合索引的前面(低基數的鍵之前)。

上一篇文章:MongoDB指南---10、索引、復合索引 簡介
下一篇文章:MongoDB指南---12、使用explain()和hint()、何時不應該使用索引

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44100.html

相關文章

  • MongoDB指南---11使用復合索引、$作符如何使用索引索引對象數組索引基數

    摘要:操作符如何使用索引有一些查詢完全無法使用索引,也有一些查詢能夠比其他查詢更高效地使用索引。有時能夠使用索引,但是通常它并不知道要如何使用索引。索引對象和數組允許深入文檔內部,對嵌套字段和數組建立索引。 上一篇文章:MongoDB指南---10、索引、復合索引 簡介下一篇文章:MongoDB指南---12、使用explain()和hint()、何時不應該使用索引 1、使用復合索引 在多...

    saucxs 評論0 收藏0
  • MongoDB指南---12、使用explain()hint()、何時不應該使用索引

    摘要:表示本次查詢使用了索引,具體來說,是使用了和上的索引,。建立索引時,或者是每執行次查詢之后,查詢優化器都會重新評估查詢計劃。上一篇文章指南使用復合索引操作符如何使用索引索引對象和數組索引基數下一篇文章指南索引類型 上一篇文章:MongoDB指南---11、使用復合索引、$操作符如何使用索引、索引對象和數組、索引基數下一篇文章:MongoDB指南---13、索引類型 使用explain...

    LiangJ 評論0 收藏0
  • MongoDB指南---12、使用explain()hint()、何時不應該使用索引

    摘要:表示本次查詢使用了索引,具體來說,是使用了和上的索引,。建立索引時,或者是每執行次查詢之后,查詢優化器都會重新評估查詢計劃。上一篇文章指南使用復合索引操作符如何使用索引索引對象和數組索引基數下一篇文章指南索引類型 上一篇文章:MongoDB指南---11、使用復合索引、$操作符如何使用索引、索引對象和數組、索引基數下一篇文章:MongoDB指南---13、索引類型 使用explain...

    DTeam 評論0 收藏0
  • MongoDB指南---10、索引復合索引 簡介

    摘要:可以通過來強制使用某個特定的索引,再次執行這個查詢,但是這次使用,作為索引。 上一篇文章:MongoDB指南---9、游標與數據庫命令下一篇文章:MongoDB指南---11、使用復合索引、$操作符如何使用索引、索引對象和數組、索引基數 本章介紹MongoDB的索引,索引可以用來優化查詢,而且在某些特定類型的查詢中,索引是必不可少的。 什么是索引?為什么要用索引? 如何選擇需要建立...

    enrecul101 評論0 收藏0
  • MongoDB指南---10、索引復合索引 簡介

    摘要:可以通過來強制使用某個特定的索引,再次執行這個查詢,但是這次使用,作為索引。 上一篇文章:MongoDB指南---9、游標與數據庫命令下一篇文章:MongoDB指南---11、使用復合索引、$操作符如何使用索引、索引對象和數組、索引基數 本章介紹MongoDB的索引,索引可以用來優化查詢,而且在某些特定類型的查詢中,索引是必不可少的。 什么是索引?為什么要用索引? 如何選擇需要建立...

    leiyi 評論0 收藏0

發表評論

0條評論

tomlingtm

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<