MongoDB索引詳解

索引能夠提高數據庫的查詢效率,沒有索引的話,查詢會進行全表掃描(scan every document in a collection),嚴重降低了查詢效率。默認情況下,Mongo在一個集合(collection)創建時,自動地對集合的_id創建了唯一索引。

1. 索引原理和特性

索引的技術實現依賴於底層的存儲引擎,在當前的版本中 MongoDB 使用 wiredTiger 作爲默認的引擎。在索引的實現上使用了 B+樹的結構,這與其他的傳統數據庫並沒有什麼不同。

2. 索引的分類

2.1 單屬性索引(Single Field)

針對單屬性索引,排序順序無關緊要,因爲MongoDB能夠在任意方向來回移動。

For a single-field index and sort operations, the sort order (i.e. ascending or descending) of the index key does not matter because MongoDB can traverse the index in either direction.

2.2 複合索引(Compound Index)

針對單複合索引,索引中key的排序順序決定了索引是否支持排序操作,舉例子:

假如:一個對象包含username和date兩個屬性,如果創建索引如下:

    db.events.createIndex( { "username" : 1, "date" : -1 } )

則查詢支持

    db.events.find().sort( { username: -1, date: 1 } )

    db.events.find().sort( { username: 1, date: -1 } ).

但是不支持如下查詢:

    db.events.find().sort( { username: 1, date: 1 } ).

2.3 多值索引(Multikey indexes)

 針對屬性包含數組數據的情況,MongoDB支持針對數組中每一個element創建索引,Multikey indexes支持strings,numbers和nested documents。

2.4地理空間索引(Geospatial Index):

 針對地理空間座標數據創建索引,類似於oracle geometry類型。

2.5 文本索引(Text Index)

MongoDB提供了針對string內容的文本查詢,Text Index支持任意屬性值爲string或string數組元素的索引查詢。註釋:一個集合僅支持最多一個Text Index。

2.6 Hashed Index

針對屬性的哈希值進行索引查詢,當要使用Hashed index時,MongoDB能夠自動的計算hash值,無需程序計算hash值。注:hash index僅支持等於查詢,不支持範圍查詢。

3. 索引屬性

3.1 唯一索引(Unique Indexes)

  即不允許屬性有重複的屬性值。

3.2部分索引(Partial Indexes)(3.2版本新增)

  對集合中指定的篩選器表達式篩選後的部分集合進行創建索引,優點:減少了存儲空間,提高的查詢效率。

3.3 稀疏索引

  索引只保存一定條目的索引屬性值,跳過沒有被指定屬性;當使用3.2之後的Mongo版本時,應優先考慮Partial Indexes。

Changed in version 3.2: Starting in MongoDB 3.2, MongoDB provides the option to create partial indexes. Partial indexes offer a superset of the functionality of sparse indexes. If you are using MongoDB 3.2 or later, partial indexes should be preferred over sparse indexes.

3.4 TTL索引

TTL索引是特殊的索引,MongoDB能夠在指定時間之後自動的刪除集合中的數據,主要應用場景爲機器產生的事件數據、日誌、會話信息等。

TTL indexes are special indexes that MongoDB can use to automatically remove documents from a collection after a certain amount of time. This is ideal for certain types of information like machine generated event data, logs, and session information that only need to persist in a database for a finite amount of time.

4. 索引的特性

在聲明索引時,還可以通過一些參數化選項來爲索引賦予一定的特性,包括:

  • unique=true,表示一個唯一性索引
  • expireAfterSeconds=3600,表示這是一個TTL索引,並且數據將在1小時後老化
  • sparse=true,表示稀疏的索引,僅索引非空(non-null)字段的文檔
  • partialFilterExpression: { rating: { $gt: 5 },條件式索引,即滿足計算條件的文檔才進行索引。

5. 索引限制

  1. 如果MongoDB的索引項超過索引限制,即1024 bytes,MongoDB將不會創建該索引,注:2.6版本之前能夠創建索引,但是不能夠對該documents進行索引;
  2. 當試圖插入一個包含索引項的屬性超過1024 bytes的documents時,MongoDB將插入documents失敗,並返回錯誤;注:2.6版本之前能夠插入成功,但是不能夠對該documents進行索引;
  3. 當試圖更新documents的屬性時時,如果索引項的屬性超過1024 bytes的,MongoDB將插入documents失敗,並返回錯誤;注:2.6版本之前能夠插入成功,但是不能夠對該documents進行索引;
  4. 如果documents存在某索引,其索引屬性超過了索引限制,則任何更新該documents將會失敗;
  5. 針對分片的collections,當數據遷移時,如果數據塊中包含索引屬性超過了索引限制,數據塊的遷移將會失敗;
  6. 一個collections最多能夠有64個索引;
  7. 針對索引的全名,包含命名空間和“.”分隔符,如:<database>.<collection name>.$<index name>,最多不超過128 characters;
  8. 針對複合索引,包含的索引屬性不能夠超過31個屬性;
  9. 查詢不能夠同時使用文本索引和地理空間索引(Queries cannot use both text and Geospatial Indexes);
  10. 包含2d sphere屬性的索引,只能夠針對地理空間屬性;
  11. 如果通過覆蓋索引查詢得到的屬性值是NaN(Not a Number),則NaN的類型總是double類型;
  12. multikey index不支持covered query。

6. 交叉索引

MongoDB可以使用多個索引的交叉來滿足查詢,通常每個交叉索引包含兩個索引,但是MongoDB能夠使用多個或嵌套索引交叉來實現查詢。

6.1 索引前綴交叉

針對交叉索引,MongoDB能夠使用交叉索引中任意一個索引的整個索引或者索引的前綴,索引前綴是指一個複合索引中索引的子集,由第一個或者前N個索引屬性中的組成;例如:

索引項如下:

{ qty: 1 }  { status: 1, ord_date: -1 }

MongoDB能夠使用如下索引:

db.orders.find( { qty: { $gt: 10 } , status: "A" } )

6.2 索引交叉與複合索引

索引交叉並不意味着複合索引沒必要存在,因爲屬性在索引中的排列順序和排序方式能夠影響到複合索引,複合索引不支持不包含索引前綴或者不同的排序方式的查詢情況(a compound index may not support a query condition that does not include theindex prefix keys or that specifies a different sort order)。例如:

如果複合索引如下:

{ status: 1, ord_date: -1 }

複合索引支持如下查詢:

db.orders.find( { status: { $in: ["A", "P" ] } } )

db.orders.find( { ord_date: { $gt: new Date("2014-02-01") }, status: {$in:[ "P", "A" ] } })

但不支持如下查詢:

db.orders.find( { ord_date: { $gt: new Date("2014-02-01") } } )db.orders.find( { } ).sort( { ord_date: 1 } )

但是如果collections包含如下索引:

{ status: 1 } { ord_date: -1 }

這兩個索引,可以通過單獨或者交叉支持以上4中查詢。

6.3 索引交叉和排序

索引交叉不支持排序操作,即要求一個索引安全的從查詢謂語分離出來的排序;例如:

collections包含如下索引:

{ qty: 1 }

{ status: 1, ord_date: -1 }

{ status: 1 }

{ ord_date: -1 }

MongoDB不支持如下帶有排序的交叉索引:

db.orders.find( { qty: { $gt: 10 } } ).sort( { status: 1 } )

That is, MongoDB does not use the { qty: 1 } index for the query, and the separate { status: 1 }or the { status: 1, ord_date: -1 } index for the sort.

7. 查詢計劃

MongoDB查詢優化器執行查詢,並針對現有的索引選擇最高效的查詢計劃,查詢系統在每次查詢執行時使用查詢計劃,查詢優化器僅緩存包含不止一種的可執行計劃的查詢計劃情況。針對每一次查詢,查詢計劃器從查詢計劃緩存中查詢一條滿足query shape的計劃,如果不存在滿足的計劃,查詢計劃器將通過試用一段時間來進行評價,來產生候選查詢計劃。查詢計劃器選擇勝出的計劃,在查詢計劃緩存中創建一個查詢計劃,然後使用該計劃產生查詢結果。

For each query, the query planner searches the query plan cache for an entry that fits the query shape. If there are no matching entries, the query planner generates candidate plans for evaluation over a trial period. The query planner chooses a winning plan, creates a cache entry containing the winning plan, and uses it to generate the result documents.If a matching entry exists, the query planner generates a plan based on that entry and evaluates its performance through a replanning mechanism. This mechanism makes a pass/fail decision based on the plan performance and either keeps or evicts the cache entry. On eviction, the query planner selects a new plan using the normal planning process and caches it. The query planner executes the plan and returns the result documents for the query

可以使用db.collection.explain()或者 the cursor.explain() 來查看一個查詢的查詢計劃統計數據。

Query shape:即查詢謂語,排序和預測詳細計劃(A combination of query predicate, sort, and projection specifications)。

查詢計劃器的執行邏輯如下:

註釋:

    a. Catalog operations(比如index的刪除或collection的刪除)將刷新查詢計劃緩存;

    b. 當Mongod重啓或者關閉後,查詢計劃器緩存將不復存在。

8. 索引過濾(Index Filters)

Index Filters決定了優化器將爲query shape評價那個索引,如果Index Filters中包含了該Index Filters,優化器將僅考慮執行Index Filters指定的索引。

When an index filter exists for the query shape, MongoDB ignores the hint(). To see whether MongoDB applied an index filter for a query shape, check the indexFilterSet field of either thedb.collection.explain() or the cursor.explain() method.

Index Filters僅影響了優化器評價哪一個索引優化器也可能仍然選擇collection 掃描以得到最優查詢計劃。

索引過濾有些類似於Oracle的RBO: Rule-Based Optimization基於規則的優化器;

註釋:

    a. 由於Index Filters覆蓋了優化器的預期的行爲和hint()方法,所以要有節制的使用index filters;

    b. Index filters在MongoDB關閉之後將不復存在,也可以使用命令刪除Index Filters。

8.覆蓋查詢(Covered Queries)

當一個查詢的查詢條件和查詢計劃中只包含索引屬性時,MongoDB不需要掃描documents或者將documents調入內存中時,這樣的查詢效率將非常高。

當同時滿足如下兩個條件時,則該查詢是Covered Queries:

      a. 查詢中的所有屬性都是索引的一部分(all the fields in the query are part of an index);

      b. 所有查詢到的結果中的屬性值,都在同一個索引中(all the fields returned in the results are in the same index)。

舉例:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章