網(wǎng)站開發(fā)團隊取什么名字好怎么引流怎么推廣自己的產(chǎn)品
概述
索引是一種用來快速查詢數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。B+Tree 就是一種常用的數(shù)據(jù)庫索引數(shù)據(jù)結(jié)構(gòu),MongoDB 采用 B+Tree 做索引,索引創(chuàng)建 colletions 上。MongoDB 不使用索引的查詢,先掃描所有的文檔,再匹配符合條件的文檔。使用索引的查詢,通過索引找到文檔,使用索引能夠極大的提升查詢效率。
MongoDB 用的數(shù)據(jù)結(jié)構(gòu)是 B-Tree,具體來說是 B+Tree,因為 B+Tree 是 B-Tree 的子集。
索引數(shù)據(jù)結(jié)構(gòu)
B-Tree 說法來源于官方文檔,然后就導(dǎo)致了分歧:有人說 MongoDB 索引數(shù)據(jù)結(jié)構(gòu)使用的是 B-Tree,有的人又說是 B+Tree。
MongoDB 官方文檔:https://docs.mongodb.com/manual/indexes/
WiredTiger官方文檔:https://source.wiredtiger.com/3.0.0/tune_page_size_and_comp.html
WiredTiger maintains a table’s data in memory using a data structure called a B-Tree ( B+ Tree to be specific), referring to the nodes of a B-Tree as pages. Internal pages carry only keys. The leaf pages store both keys and values.
WiredTiger 數(shù)據(jù)文件在磁盤的存儲結(jié)構(gòu):
B+Tree 中的 leaf page 包含一個頁頭(page header)、塊頭(block header)和真正的數(shù)據(jù)(key/value),其中頁頭定義了頁的類型、頁中實際載荷數(shù)據(jù)的大小、頁中記錄條數(shù)等信息;塊頭定義了此頁的 checksum、塊在磁盤上的尋址位置等信息。
WiredTiger 有一個塊設(shè)備管理的模塊,用來為 page 分配 block。如果要定位某一行數(shù)據(jù)(key/value)的位置,可以先通過 block 的位置找到此 page(相對于文件起始位置的偏移量),再通過 page 找到行數(shù)據(jù)的相對位置,最后可以得到行數(shù)據(jù)相對于文件起始位置的偏移量 offsets。
索引操作
創(chuàng)建索引
創(chuàng)建索引語法格式:
db.collection.createIndex(keys, options)
Key 值為你要創(chuàng)建的索引字段,1 按升序創(chuàng)建索引, -1 按降序創(chuàng)建索引。
Key 可選參數(shù)列表如下:
Parameter | Type | Description |
---|---|---|
background | Boolean | 建索引過程會阻塞其它數(shù)據(jù)庫操作,background 可指定以后臺方式創(chuàng)建索引,即增加 “background” 可選參數(shù)。 “background” 默認(rèn)值為 false。 |
unique | Boolean | 建立的索引是否唯一。指定為 true 創(chuàng)建唯一索引。默認(rèn)值為 false。 |
name | string | 索引的名稱。如果未指定,MongoDB 的通過連接索引的字段名和排序順序生成一個索引名稱。 |
dropDups | Boolean | 3.0+ 版本已廢棄。在建立唯一索引時是否刪除重復(fù)記錄,指定 true 創(chuàng)建唯一索引。默認(rèn)值為 false。 |
sparse | Boolean | 對文檔中不存在的字段數(shù)據(jù)不啟用索引。這個參數(shù)需要特別注意,如果設(shè)置為 true 的話,在索引字段中不會查詢出不包含對應(yīng)字段的文檔。默認(rèn)值為 false。 |
expireAfterSeconds | integer | 指定一個以秒為單位的數(shù)值,完成 TTL 設(shè)定,設(shè)定集合的生存時間。 |
v | index version | 索引的版本號。默認(rèn)的索引版本取決于 mongodb 創(chuàng)建索引時運行的版本。 |
weights | document | 索引權(quán)重值,數(shù)值在 1 到 99,999 之間,表示該索引相對于其他索引字段的得分權(quán)重。 |
default_language | string | 對于文本索引,該參數(shù)決定了停用詞及詞干和詞器的規(guī)則的列表。 默認(rèn)為英語。 |
language_override | string | 對于文本索引,該參數(shù)指定了包含在文檔中的字段名,語言覆蓋默認(rèn)的 language,默認(rèn)值為 language。 |
注意:3.0.0 版本前創(chuàng)建索引方法為 db.collection.ensureIndex()
。
# 創(chuàng)建索引后臺執(zhí)行
db.values.createIndex({open: 1, close: 1}, {background: true})
# 創(chuàng)建唯一索引
db.values.createIndex({title:1},{unique:true})
查看索引
# 查看索引信息
db.books.getIndexes()
# 查看索引鍵
db.books.getIndexKeys()
刪除索引
# 刪除集合指定索引
db.col.dropIndex("索引名稱")
# 刪除集合所有索引,不能刪除主鍵索引
db.col.dropIndexes()
索引類型
與大多數(shù)數(shù)據(jù)庫一樣,MongoDB 支持各種豐富的索引類型,包括單鍵索引、復(fù)合索引,唯一索引等一些常用的結(jié)構(gòu)。由于采用了靈活可變的文檔類型,因此它也同樣支持對嵌套字段、數(shù)組進行索引。通過建立合適的索引,我們可以極大地提升數(shù)據(jù)的檢索速度。在一些特殊應(yīng)用場景,MongoDB 還支持地理空間索引、文本檢索索引、TTL 索引等不同的特性。
單鍵索引(Single Field Indexes)
在某一個特定的字段上建立索引。MongoDB 在 ID 上建立了唯一的單鍵索引,所以經(jīng)常會使用 id 來進行查詢。在索引字段上進行精確匹配、排序以及范圍查找都會使用此索引。
db.books.createIndex({title:1})
# 對內(nèi)嵌文檔字段創(chuàng)建索引
db.books.createIndex({"author.name":1})
復(fù)合索引(Compound Index)
復(fù)合索引是多個字段組合而成的索引,其性質(zhì)和單字段索引類似。但不同的是,復(fù)合索引中字段的順序、字段的升降序?qū)Σ樵冃阅苡兄苯拥挠绊?#xff0c;因此在設(shè)計復(fù)合索引時則需要考慮不同的查詢場景。
db.books.createIndex({type:1,favCount:1})
# 查看執(zhí)行計劃
db.books.find({type:"novel",favCount:{$gt:50}}).explain()
多鍵(數(shù)組)索引(Multikey Index)
在數(shù)組的屬性上建立索引。針對這個數(shù)組的任意值的查詢都會定位到這個文檔,既多個索引入口或者鍵值引用同一個文檔。
準(zhǔn)備 inventory 集合:
db.inventory.insertMany([
{ _id: 5, type: "food", item: "aaa", ratings: [ 5, 8, 9 ] },
{ _id: 6, type: "food", item: "bbb", ratings: [ 5, 9 ] },
{ _id: 7, type: "food", item: "ccc", ratings: [ 9, 5, 8 ] },
{ _id: 8, type: "food", item: "ddd", ratings: [ 9, 5 ] },
{ _id: 9, type: "food", item: "eee", ratings: [ 5, 9, 5 ] }
])
創(chuàng)建多鍵索引:
db.inventory.createIndex( { ratings: 1 } )
多鍵索引很容易與復(fù)合索引產(chǎn)生混淆,復(fù)合索引是多個字段的組合,而多鍵索引則僅僅是在一個字段上出現(xiàn)了多鍵(multi key)。而實質(zhì)上,多鍵索引也可以出現(xiàn)在復(fù)合字段上。
# 創(chuàng)建復(fù)合多值索引
db.inventory.createIndex( { item:1,ratings: 1 } )
注意:MongoDB 并不支持一個復(fù)合索引中同時出現(xiàn)多個數(shù)組字段。
嵌入文檔的索引數(shù)組:
db.inventory.insertMany([
{_id: 1,item: "abc",stock: [{ size: "S", color: "red", quantity: 25 },{ size: "S", color: "blue", quantity: 10 },{ size: "M", color: "blue", quantity: 50 }]
},
{_id: 2,item: "def",stock: [{ size: "S", color: "blue", quantity: 20 },{ size: "M", color: "blue", quantity: 5 },{ size: "M", color: "black", quantity: 10 },{ size: "L", color: "red", quantity: 2 }]
},
{_id: 3,item: "ijk",stock: [{ size: "M", color: "blue", quantity: 15 },{ size: "L", color: "blue", quantity: 100 },{ size: "L", color: "red", quantity: 25 }]
}
])
在包含嵌套對象的數(shù)組字段上創(chuàng)建多鍵索引:
db.inventory.createIndex( { "stock.size": 1, "stock.quantity": 1 } )db.inventory.find({"stock.size":"S","stock.quantity":{$gt:20}}).explain()
Hash 索引(Hashed Indexes)
不同于傳統(tǒng)的 B-Tree 索引,哈希索引使用 hash 函數(shù)來創(chuàng)建索引。在索引字段上進行精確匹配,但不支持范圍查詢,不支持多鍵 hash。Hash 索引上的入口是均勻分布的,在分片集合中非常有用。
db.users.createIndex({username : 'hashed'})
地理空間索引(Geospatial Index)
在移動互聯(lián)網(wǎng)時代,基于地理位置的檢索(LBS)功能幾乎是所有應(yīng)用系統(tǒng)的標(biāo)配。MongoDB 為地理空間檢索提供了非常方便的功能。地理空間索引(2dsphereindex)就是專門用于實現(xiàn)位置檢索的一種特殊索引。
案例:MongoDB 如何實現(xiàn)“查詢附近商家"?
假設(shè)商家的數(shù)據(jù)模型如下:
db.restaurant.insert({restaurantId: 0,restaurantName:"蘭州牛肉面",location : {type: "Point",coordinates: [ -73.97, 40.77 ]}
})
創(chuàng)建一個 2dsphere 索引:
db.restaurant.createIndex({location : "2dsphere"})
查詢附近 10000 米商家信息:
db.restaurant.find( { location:{ $near :{$geometry :{ type : "Point" ,coordinates : [ -73.88, 40.78 ] } ,$maxDistance:10000 } }
} )
$near 查詢操作符,用于實現(xiàn)附近商家的檢索,返回數(shù)據(jù)結(jié)果會按距離排序。
$geometry 操作符用于指定一個 GeoJSON 格式的地理空間對象,type=Point 表示地理坐標(biāo)點,coordinates 則是用戶當(dāng)前所在的經(jīng)緯度位置。
$maxDistance 限定了最大距離,單位是米。
全文索引(Text Indexes)
MongoDB 支持全文檢索功能,可通過建立文本索引來實現(xiàn)簡易的分詞檢索。
db.reviews.createIndex( { comments: "text" } )
t e x t 操作符可以在有 t e x t i n d e x 的集合上執(zhí)行文本檢索。 text 操作符可以在有 text index 的集合上執(zhí)行文本檢索。 text操作符可以在有textindex的集合上執(zhí)行文本檢索。text 將會使用空格和標(biāo)點符號作為分隔符對檢索字符串進行分詞,并且對檢索字符串中所有的分詞結(jié)果進行一個邏輯上的 OR 操作。
全文索引能解決快速文本查找的需求,比如有一個博客文章集合,需要根據(jù)博客的內(nèi)容來快速查找,則可以針對博客內(nèi)容建立文本索引。
案例
數(shù)據(jù)準(zhǔn)備:
db.stores.insert([{ _id: 1, name: "Java Hut", description: "Coffee and cakes" },{ _id: 2, name: "Burger Buns", description: "Gourmet hamburgers" },{ _id: 3, name: "Coffee Shop", description: "Just coffee" },{ _id: 4, name: "Clothes Clothes Clothes", description: "Discount clothing" },{ _id: 5, name: "Java Shopping", description: "Indonesian goods" }]
)
創(chuàng)建 name 和 description 的全文索引:
db.stores.createIndex({name: "text", description: "text"})
測試:
通過 $text 操作符來查尋數(shù)據(jù)中所有包含“coffee”、“shop”、“java”列表中任何詞語的商店:
db.stores.find({$text: {$search: "java coffee shop"}})
MongoDB 的文本索引功能存在諸多限制,而官方并未提供中文分詞的功能,這使得該功能的應(yīng)用場景十分受限。
通配符索引(Wildcard Indexes)
MongoDB 的文檔模式是動態(tài)變化的,而通配符索引可以建立在一些不可預(yù)知的字段上,以此實現(xiàn)查詢的加速。MongoDB 4.2 引入了通配符索引來支持對未知或任意字段的查詢。
案例
準(zhǔn)備商品數(shù)據(jù),不同商品屬性不一樣:
db.products.insert([{"product_name" : "Spy Coat","product_attributes" : {"material" : [ "Tweed", "Wool", "Leather" ],"size" : {"length" : 72,"units" : "inches"}}},{"product_name" : "Spy Pen","product_attributes" : {"colors" : [ "Blue", "Black" ],"secret_feature" : {"name" : "laser","power" : "1000","units" : "watts",}}},{"product_name" : "Spy Book"}
])
創(chuàng)建通配符索引:
db.products.createIndex( { "product_attributes.$**" : 1 } )
測試:
通配符索引可以支持任意單字段查詢 product_attributes 或其嵌入字段。
db.products.find( { "product_attributes.size.length" : { $gt : 60 } } )
db.products.find( { "product_attributes.material" : "Leather" } )
db.products.find( { "product_attributes.secret_feature.name" : "laser" } )
注意事項
- 通配符索引不兼容的索引類型或?qū)傩?/li>
- 通配符索引是稀疏的,不索引空字段。因此,通配符索引不能支持查詢字段不存在的文檔。
# 通配符索引不能支持以下查詢
db.products.find( {"product_attributes" : { $exists : false } } )
db.products.aggregate([{ $match : { "product_attributes" : { $exists : false } } }
])
- 通配符索引為文檔或數(shù)組的內(nèi)容生成條目,而不是文檔/數(shù)組本身。因此通配符索引不能支持精確的文檔/數(shù)組相等匹配。通配符索引可以支持查詢字段等于空文檔{}的情況。
# 通配符索引不能支持以下查詢:
db.products.find({ "product_attributes.colors" : [ "Blue", "Black" ] } )db.products.aggregate([{$match : { "product_attributes.colors" : [ "Blue", "Black" ] }
}])
索引屬性
唯一索引(Unique Indexes)
在現(xiàn)實場景中,唯一性是很常見的一種索引約束需求,重復(fù)的數(shù)據(jù)記錄會帶來許多處理上的麻煩,比如訂單的編號、用戶的登錄名等。通過建立唯一性索引,可以保證集合中文檔的指定字段擁有唯一值。
# 創(chuàng)建唯一索引
db.values.createIndex({title:1},{unique:true})
# 復(fù)合索引支持唯一性約束
db.values.createIndex({title:1,type:1},{unique:true})
# 多鍵索引支持唯一性約束
db.inventory.createIndex( { ratings: 1 },{unique:true} )
- 唯一性索引對于文檔中缺失的字段,會使用 null 值代替,因此不允許存在多個文檔缺失索引字段的情況。
- 對于分片的集合,唯一性約束必須匹配分片規(guī)則。換句話說,為了保證全局的唯一性,分片鍵必須作為唯一性索引的前綴字段。
部分索引(Partial Indexes)
部分索引僅對滿足指定過濾器表達式的文檔進行索引。通過在一個集合中為文檔的一個子集建立索引,部分索引具有更低的存儲需求和更低的索引創(chuàng)建和維護的性能成本。3.2 新版功能。
部分索引提供了稀疏索引功能的超集,應(yīng)該優(yōu)先于稀疏索引。
db.restaurants.createIndex({ cuisine: 1, name: 1 },{ partialFilterExpression: { rating: { $gt: 5 } } }
)
partialFilterExpression 選項接受指定過濾條件的文檔:
等式表達式(例如:field: value 或使用 $eq 操作符)
$exists: true
$gt, $gte, $lt, $lte
$type
頂層的 $and
# 符合條件,使用索引
db.restaurants.find( { cuisine: "Italian", rating: { $gte: 8 } } )
# 不符合條件,不能使用索引
db.restaurants.find( { cuisine: "Italian" } )
唯一約束結(jié)合部分索引使用導(dǎo)致唯一約束失效的問題:
如果同時指定了 partialFilterExpression 和唯一約束,那么唯一約束只適用于滿足篩選器表達式的文檔。如果文檔不滿足篩選條件,那么帶有惟一約束的部分索引不會阻止插入不滿足惟一約束的文檔。
稀疏索引(Sparse Indexes)
索引的稀疏屬性確保索引只包含具有索引字段的文檔的條目,索引將跳過沒有索引字段的文檔。
特性:只對存在字段的文檔進行索引(包括字段值為 null 的文檔)。
# 不索引不包含xmpp_id字段的文檔
db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } )
如果稀疏索引會導(dǎo)致查詢和排序操作的結(jié)果集不完整,MongoDB 將不會使用該索引,除非 hint() 明確指定索引。
案例
數(shù)據(jù)準(zhǔn)備:
db.scores.insertMany([{"userid" : "newbie"},{"userid" : "abby", "score" : 82},{"userid" : "nina", "score" : 90}
])
創(chuàng)建稀疏索引:
db.scores.createIndex( { score: 1 } , { sparse: true } )
測試:
# 使用稀疏索引
db.scores.find( { score: { $lt: 90 } } )# 即使排序是通過索引字段,MongoDB也不會選擇稀疏索引來完成查詢,以返回完整的結(jié)果
db.scores.find().sort( { score: -1 } )# 要使用稀疏索引,使用hint()顯式指定索引
db.scores.find().sort( { score: -1 } ).hint( { score: 1 } )
同時具有稀疏性和唯一性的索引可以防止集合中存在字段值重復(fù)的文檔,但允許不包含此索引字段的文檔插入。
TTL索引(TTL Indexes)
在一般的應(yīng)用系統(tǒng)中,并非所有的數(shù)據(jù)都需要永久存儲。例如一些系統(tǒng)事件、用戶消息等,這些數(shù)據(jù)隨著時間的推移,其重要程度逐漸降低。更重要的是,存儲這些大量的歷史數(shù)據(jù)需要花費較高的成本,因此項目中通常會對過期且不再使用的數(shù)據(jù)進行老化處理。
通常的做法如下:
方案一:為每個數(shù)據(jù)記錄一個時間戳,應(yīng)用側(cè)開啟一個定時器,按時間戳定期刪除過期的數(shù)據(jù)。
方案二:數(shù)據(jù)按日期進行分表,同一天的數(shù)據(jù)歸檔到同一張表,同樣使用定時器刪除過期的表。
對于數(shù)據(jù)老化,MongoDB 提供了一種更加便捷的做法:TTL(Time To Live)索引。TTL 索引需要聲明在一個日期類型的字段中,TTL 索引是特殊的單字段索引,MongoDB 可以使用它在一定時間或特定時鐘時間后自動從集合中刪除文檔。
# 創(chuàng)建 TTL 索引,TTL 值為3600秒
db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } )
對集合創(chuàng)建 TTL 索引之后,MongoDB 會在周期性運行的后臺線程中對該集合進行檢查及數(shù)據(jù)清理工作。除了數(shù)據(jù)老化功能,TTL 索引具有普通索引的功能,同樣可以用于加速數(shù)據(jù)的查詢。
TTL 索引不保證過期數(shù)據(jù)會在過期后立即被刪除。文檔過期和 MongoDB 從數(shù)據(jù)庫中刪除文檔的時間之間可能存在延遲。刪除過期文檔的后臺任務(wù)每 60 秒運行一次。因此,在文檔到期和后臺任務(wù)運行之間的時間段內(nèi),文檔可能會保留在集合中。
案例
數(shù)據(jù)準(zhǔn)備:
db.log_events.insertOne( {"createdAt": new Date(),"logEvent": 2,"logMessage": "Success!"
} )
創(chuàng)建 TTL 索引:
db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 20 } )
可變的過期時間
TTL 索引在創(chuàng)建之后,仍然可以對過期時間進行修改。這需要使用 collMod 命令對索引的定義進行變更:
db.runCommand({collMod:"log_events",index:{keyPattern:{createdAt:1},expireAfterSeconds:600}})
使用約束
TTL 索引的確可以減少開發(fā)的工作量,而且通過數(shù)據(jù)庫自動清理的方式會更加高效、可靠,但是在使用 TTL 索引時需要注意以下的限制:
- TTL 索引只能支持單個字段,并且必須是非 _id 字段。
- TTL 索引不能用于固定集合。
- TTL 索引無法保證及時的數(shù)據(jù)老化,MongoDB 會通過后臺的 TTLMonitor 定時器來清理老化數(shù)據(jù),默認(rèn)的間隔時間是 1 分鐘。當(dāng)然如果在數(shù)據(jù)庫負載過高的情況下,TTL 的行為則會進一步受到影響。
- TTL 索引對于數(shù)據(jù)的清理僅僅使用了 remove 命令,這種方式并不是很高效。因此 TTL Monitor 在運行期間對系統(tǒng) CPU、磁盤都會造成一定的壓力。相比之下,按日期分表的方式操作會更加高效。
隱藏索引(Hidden Indexes)
隱藏索引對查詢規(guī)劃器不可見,不能用于支持查詢。通過對規(guī)劃器隱藏索引,用戶可以在不實際刪除索引的情況下評估刪除索引的潛在影響。如果影響是負面的,用戶可以取消隱藏索引,而不必重新創(chuàng)建已刪除的索引。4.4 新版功能。
# 創(chuàng)建隱藏索引
db.restaurants.createIndex({ borough: 1 },{ hidden: true });
# 隱藏現(xiàn)有索引
db.restaurants.hideIndex( { borough: 1} );
db.restaurants.hideIndex( "索引名稱" )
# 取消隱藏索引
db.restaurants.unhideIndex( { borough: 1} );
db.restaurants.unhideIndex( "索引名稱" );
案例
數(shù)據(jù)準(zhǔn)備:
db.scores.insertMany([{"userid" : "newbie"},{"userid" : "abby", "score" : 82},{"userid" : "nina", "score" : 90}
])
創(chuàng)建隱藏索引:
db.scores.createIndex({ userid: 1 },{ hidden: true }
)
查看索引信息:
db.scores.getIndexes()
索引屬性 hidden 只在值為 true 時返回。
測試:
# 不使用索引
db.scores.find({userid:"abby"}).explain()
# 取消隱藏索引
db.scores.unhideIndex( { userid: 1} )
# 使用索引
db.scores.find({userid:"abby"}).explain()
索引使用建議
1)為每一個查詢建立合適的索引
這個是針對于數(shù)據(jù)量較大比如說超過幾十上百萬(文檔數(shù)目)數(shù)量級的集合。如果沒有索引 MongoDB 需要把所有的 Document 從盤上讀到內(nèi)存,這會對 MongoDB 服務(wù)器造成較大的壓力并影響到其他請求的執(zhí)行。
2)創(chuàng)建合適的復(fù)合索引,不要依賴于交叉索引
如果你的查詢會使用到多個字段,MongoDB 有兩個索引技術(shù)可以使用:交叉索引和復(fù)合索引。交叉索引就是針對每個字段單獨建立一個單字段索引,然后在查詢執(zhí)行時候使用相應(yīng)的單字段索引進行索引交叉而得到查詢結(jié)果。交叉索引目前觸發(fā)率較低,所以如果你有一個多字段查詢的時候,建議使用復(fù)合索引能夠保證索引正常的使用。
# 查找所有年齡小于30歲的深圳市馬拉松運動員
db.athelets.find({sport: "marathon", location: "sz", age: {$lt: 30}}})
# 創(chuàng)建復(fù)合索引
db.athelets.createIndex({sport:1, location:1, age:1})
3)復(fù)合索引字段順序:匹配條件在前,范圍條件在后(Equality First, Range After)
前面的例子,在創(chuàng)建復(fù)合索引時如果條件有匹配和范圍之分,那么匹配條件(sport: “marathon”) 應(yīng)該在復(fù)合索引的前面。范圍條件(age: <30)字段應(yīng)該放在復(fù)合索引的后面。
4)盡可能使用覆蓋索引(Covered Index)
建議只返回需要的字段,同時,利用覆蓋索引來提升性能。
5)建索引要在后臺運行
在對一個集合創(chuàng)建索引時,該集合所在的數(shù)據(jù)庫將不接受其他讀寫操作。對大數(shù)據(jù)量的集合建索引,建議使用后臺運行選項 {background: true}
。
6)避免設(shè)計過長的數(shù)組索引
數(shù)組索引是多值的,在存儲時需要使用更多的空間。如果索引的數(shù)組長度特別長,或者數(shù)組的增長不受控制,則可能導(dǎo)致索引空間急劇膨脹。
explain 執(zhí)行計劃
通常我們需要關(guān)心的問題:
- 查詢是否使用了索引
- 索引是否減少了掃描的記錄數(shù)量
- 是否存在低效的內(nèi)存排序
MongoDB 提供了 explain 命令,它可以幫助我們評估指定查詢模型(querymodel)的執(zhí)行計劃,根據(jù)實際情況進行調(diào)整,然后提高查詢效率。
explain() 方法的形式如下:
db.collection.find().explain(<verbose>)
verbose:可選參數(shù),表示執(zhí)行計劃的輸出模式,默認(rèn) queryPlanner
verbose 枚舉如下:
**模式名字 ** | 描述 |
---|---|
queryPlanner | 執(zhí)行計劃的詳細信息,包括查詢計劃、集合信息、查詢條件、最佳執(zhí)行計劃、查詢方式和 MongoDB 服務(wù)信息等。 |
exectionStats | 最佳執(zhí)行計劃的執(zhí)行情況和被拒絕的計劃等信息。 |
allPlansExecution | 選擇并執(zhí)行最佳執(zhí)行計劃,并返回最佳執(zhí)行計劃和其他執(zhí)行計劃的執(zhí)行情況。 |
queryPlanner
# 未創(chuàng)建title的索引
db.books.find({title:"book-1"}).explain("queryPlanner")
結(jié)果:
字段說明:
**字段名稱 ** | 描述 |
---|---|
plannerVersion | 執(zhí)行計劃的版本 |
namespace | 查詢的集合 |
indexFilterSet | 是否使用索引 |
parsedQuery | 查詢條件 |
winningPlan | 最佳執(zhí)行計劃 |
stage | 查詢方式 |
filter | 過濾條件 |
direction | 查詢順序 |
rejectedPlans | 拒絕的執(zhí)行計劃 |
serverInfo | mongodb 服務(wù)器信息 |
executionStats
executionStats 模式的返回信息中包含了 queryPlanner 模式的所有字段,并且還包含了最佳執(zhí)行計劃的執(zhí)行情況
# 創(chuàng)建索引
db.books.createIndex({title:1})db.books.find({title:"book-1"}).explain("executionStats")
結(jié)果:
字段說明:
**字段名稱 ** | 描述 |
---|---|
winningPlan.inputStage | 用來描述子 stage,并且為其父 stage 提供文檔和索引關(guān)鍵字 |
winningPlan.inputStage.stage | 子查詢方式 |
winningPlan.inputStage.keyPattern | 所掃描的 index 內(nèi)容 |
winningPlan.inputStage.indexName | 索引名 |
winningPlan.inputStage.isMultiKey | 是否是 Multikey。如果索引建立在 array 上,將是 true |
executionStats.executionSuccess | 是否執(zhí)行成功 |
executionStats.nReturned | 返回的個數(shù) |
executionStats.executionTimeMillis | 這條語句執(zhí)行時間 |
executionStats.executionStages.executionTimeMillisEstimate | 檢索文檔獲取數(shù)據(jù)的時間 |
executionStats.executionStages.inputStage.executionTimeMillisEstimate | 掃描獲取數(shù)據(jù)的時間 |
executionStats.totalKeysExamined | 索引掃描次數(shù) |
executionStats.totalDocsExamined | 文檔掃描次數(shù) |
executionStats.executionStages.isEOF | 是否到達 steam 結(jié)尾,1 或者 true 代表已到達結(jié)尾 |
executionStats.executionStages.works | 工作單元數(shù),一個查詢會分解成小的工作單元 |
executionStats.executionStages.advanced | 優(yōu)先返回的結(jié)果數(shù) |
executionStats.executionStages.docsExamined | 文檔檢查數(shù) |
allPlansExecution
allPlansExecution 返回的信息包含 executionStats 模式的內(nèi)容,且包含 allPlansExecution:[]
塊
"allPlansExecution" : [{"nReturned" : <int>,"executionTimeMillisEstimate" : <int>,"totalKeysExamined" : <int>,"totalDocsExamined" :<int>,"executionStages" : {"stage" : <STAGEA>,"nReturned" : <int>,"executionTimeMillisEstimate" : <int>,...}}},...]
stage 狀態(tài)
**狀態(tài) ** | 描述 |
---|---|
COLLSCAN | 全表掃描 |
IXSCAN | 索引掃描 |
FETCH | 根據(jù)索引檢索指定文檔 |
SHARD_MERGE | 將各個分片返回數(shù)據(jù)進行合并 |
SORT | 在內(nèi)存中進行了排序 |
LIMIT | 使用 limit 限制返回數(shù) |
SKIP | 使用 skip 進行跳過 |
IDHACK | 對 _id 進行查詢 |
SHARDING_FILTER | 通過 mongos 對分片數(shù)據(jù)進行查詢 |
COUNTSCAN | count 不使用 Index 進行 count 時的 stage 返回 |
COUNT_SCAN | count 使用了 Index 進行 count 時的 stage 返回 |
SUBPLA | 未使用到索引的 $or 查詢的 stage 返回 |
TEXT | 使用全文索引進行查詢時候的 stage 返回 |
PROJECTION | 限定返回字段時候 stage 的返回 |
執(zhí)行計劃的返回結(jié)果中盡量不要出現(xiàn)以下 stage:
- COLLSCAN(全表掃描)
- SORT(使用 sort 但是無 index)
- 不合理的 SKIP
- SUBPLA(未用到 index 的 $or)
- COUNTSCAN(不使用 index 進行 count)