本篇文章帶你了解mongodb,介紹一下mongodb中豐富的索引類型,希望對大家有所幫助!
MongoDB的索引和mysql的索引的作用和優化要遵循的原則基本相似,MySql索引類型基本可以區分為:
- 單鍵索引 – 聯合索引
- 主鍵索引(聚簇索引) – 非主鍵索引(非聚簇索引)
在MongoDB中除了這些基礎的分類之外,還有一些特殊的索引類型,如: 數組索引 | 稀疏索引 | 地理空間索引 | TTL索引等.
為了下面方便測試我們使用腳本插入以下數據
for(var?i?=?0;i?<h3 data-id="heading-0"><strong>單鍵索引</strong></h3><p>單鍵索引即索引的字段只有一個,是最基礎的索引方式.</p><p>在集合中使用username字段,創建一個單鍵索引,MongoDB會自動將這個索引命名為username_1</p><pre class="brush:js;toolbar:false;">db.users.createIndex({username:1}) 'username_1'
在創建索引后查看一下使用username字段的查詢計劃,stage為IXSCAN代表使用使用了索引掃描
db.users.find({username:"user40001"}).explain() {? ???queryPlanner:? ???{? ?????winningPlan:? ?????{? ????????...... ????????stage:?'FETCH', ????????inputStage:? ????????{? ???????????stage:?'IXSCAN', ???????????keyPattern:?{?username:?1?}, ???????????indexName:?'username_1', ???????????...... ????????}? ?????} ?????rejectedPlans:?[]?, ???}, ???...... ???ok:?1? }
? ? ? ? 在索引優化的原則當中,有很重要的原則就是索引要建立在基數高的的字段上,所謂基數就是一個字段上不重復數值的個數,即我們在創建users集合時年齡出現的數值是0-99那么age這個字段將會有100個不重復的數值,即age字段的基數為100,而sex這個字段只會出現0 | 1這個兩個值,即sex字段的基礎是2,這是一個相當低的基數,在這種情況下,索引的效率并不高并且會導致索引失效.
下面就船艦一個sex字段索引,來查詢執行計劃會發現,查詢時是走的全表掃描,而沒有走相關索引.
db.users.createIndex({sex:1}) 'sex_1' db.users.find({sex:1}).explain() {? ??queryPlanner:? ??{? ?????...... ?????winningPlan:? ?????{? ????????stage:?'COLLSCAN', ????????filter:?{?sex:?{?'$eq':?1?}?}, ????????direction:?'forward'? ?????}, ?????rejectedPlans:?[]? ??}, ??...... ??ok:?1? }
聯合索引
聯合索引即索引上會有多個字段,下面使用age和sex兩個字段創建一個索引
db.users.createIndex({age:1,sex:1}) 'age_1_sex_1'
然后我們使用這兩個字段進行一次查詢,查看執行計劃,順利地走了這條索引
db.users.find({age:23,sex:1}).explain() {? ??queryPlanner:? ??{? ?????...... ?????winningPlan:? ?????{? ????????stage:?'FETCH', ????????inputStage:? ????????{? ???????????stage:?'IXSCAN', ???????????keyPattern:?{?age:?1,?sex:?1?}, ???????????indexName:?'age_1_sex_1', ???????????....... ???????????indexBounds:?{?age:?[?'[23,?23]'?],?sex:?[?'[1,?1]'?]?}? ????????}? ?????}, ?????rejectedPlans:?[],? ??}, ??...... ??ok:?1? ?}
數組索引
數組索引就是對數組字段創建索引,也叫做多值索引,下面為了測試將users集合中的數據增加一部分數組字段.
db.users.updateOne({username:"user1"},{$set:{hobby:["唱歌","籃球","rap"]}}) ......
創建數組索引并進行查看其執行計劃,注意isMultiKey: true表示使用的索引是多值索引.
db.users.createIndex({hobby:1}) 'hobby_1' db.users.find({hobby:{$elemMatch:{$eq:"釣魚"}}}).explain() {? ???queryPlanner:? ???{? ?????...... ?????winningPlan:? ?????{? ????????stage:?'FETCH', ????????filter:?{?hobby:?{?'$elemMatch':?{?'$eq':?'釣魚'?}?}?}, ????????inputStage:? ????????{? ???????????stage:?'IXSCAN', ???????????keyPattern:?{?hobby:?1?}, ???????????indexName:?'hobby_1', ???????????isMultiKey:?true, ???????????multiKeyPaths:?{?hobby:?[?'hobby'?]?}, ???????????...... ???????????indexBounds:?{?hobby:?[?'["釣魚",?"釣魚"]'?]?}?}? ?????????}, ?????rejectedPlans:?[]? ??}, ??...... ??ok:?1? }
? ? ? ? 數組索引相比于其它索引來說索引條目和體積必然呈倍數增加,例如平均每個文檔的hobby數組的size為10,那么這個集合的hobby數組索引的條目數量將是普通索引的10倍.
聯合數組索引
? ? ? ? 聯合數組索引就是含有數組字段的聯合索引,這種索引不支持一個索引中含有多個數組字段,即一個索引中最多能有一個數組字段,這是為了避免索引條目爆炸式增長,假設一個索引中有兩個數組字段,那么這個索引條目的數量將是普通索引的n*m倍
地理空間索引
在原先的users集合上,增加一些地理信息
for(var?i?=?0;i?<p>創建一個二維空間索引</p><pre class="brush:js;toolbar:false;">db.users.createIndex({location:"2dsphere"}) 'location_2dsphere' //查詢500米內的人 db.users.find({ ??location:{ ????$near:{ ??????$geometry:{type:"Point",coordinates:[102,41.5]}, ??????$maxDistance:500 ????} ??} })
地理空間索引的type有很多包含Ponit(點) | LineString(線) | Polygon(多邊形) 等
TTL索引
? ? ? ? TTL的全拼是time to live,主要是用于過期數據自動刪除,使用這種索引需要在文檔中聲明一個時間類型的字段,然后為這個字段創建TTL索引的時候還需要設置一個expireAfterSeconds過期時間單位為秒,創建完成后MongoDB會定期對集合中的數據進行檢查,當出現:
MongoDB將會自動將這些文檔刪除,這種索引還有以下這些要求:
- TTL索引只能有一個字段,沒有聯合TTL索引
- TTL不能用于固定集合
- TTL索引是逐個遍歷后,發現滿足刪除條件會使用delete函數刪除,效率并不高
首先在我們文檔上增減一個時間字段
for(var?i?=?90000;i?<p>創建一個TTL索引并且設定過期時間為60s,待過60s后查詢,會發現這些數據已經不存在</p><pre class="brush:js;toolbar:false;">db.users.createIndex({createdDate:1},{expireAfterSeconds:60}) 'createdDate_1'
另外還可以用CollMod命令更改TTL索引的過期時間
db.runCommand({ ??collMod:"users", ??index:{ ????keyPattern:{createdDate:1}, ????expireAfterSeconds:120 ??} }) {?expireAfterSeconds_old:?60,?expireAfterSeconds_new:?120,?ok:?1?}
條件索引
條件索引也叫部分索引(partial),只對滿足條件的數據進行建立索引.
只對50歲以上的user進行建立username_1索引,查看執行計劃會發現isPartial這個字段會變成true
db.users.createIndex({username:1},{partialFilterExpression:{ ????age:{$gt:50} ??}}) 'username_1' db.users.find({$and:[{username:"user4"},{age:60}]}).explain() {? ??queryPlanner:? ??{? ?????...... ?????winningPlan:? ?????{? ????????stage:?'FETCH', ????????filter:?{?age:?{?'$eq':?60?}?}, ????????inputStage:? ????????{? ???????????stage:?'IXSCAN', ???????????keyPattern:?{?username:?1?}, ???????????indexName:?'username_1', ???????????...... ???????????isPartial:?true, ???????????...... ?????????}? ?????}, ?????rejectedPlans:?[]? ??}, ??...... ??ok:?1? }
稀疏索引
? ? ? ? 一般的索引會根據某個字段為整個集合創建一個索引,即使某個文檔不存這個字段,那么這個索引會把這個文檔的這個字段當作NULL建立在索引當中.
稀疏索引不會對文檔中不存在的字段建立索引,如果這個字段存在但是為null時,則會創建索引.
下面給users集合中的部分數據創建稀疏索引
for(var?i?=?5000;i?<p>當不建立索引使用{email:null}條件進行查詢時,我們會發現查出來的文檔包含沒有email字段的文檔</p><pre class="brush:js;toolbar:false;">db.users.find({email:null}) {? ??_id:?ObjectId("61bdc01ba59136670f6536fd"), ??username:?'user0', ??age:?64.41483801726282, ??sex:?0, ??phone:?18468150001, ??location:? ??{? ????type:?'Point', ????coordinates:?[?101.42490900320335,?42.2576650823515?]? ??}? } ......
? ? ? ? 然后對email這個字段創建一個稀疏索引使用{email:null}條件進行查詢,則發現查詢來的文檔全部是email字段存在且為null的文檔.
db.users.createIndex({email:1},{sparse:true}); 'email_1' db.users.find({email:null}).hint({email:1}) {? ??_id:?ObjectId("61bdc12ca59136670f655a25"), ??username:?'user9000', ??age:?94.18397576757012, ??sex:?0, ??phone:?18468159001, ??hobby:?[?'釣魚',?'乒乓球'?], ??location:? ??{? ????type:?'Point', ????coordinates:?[?101.25903151863596,?41.38450145025062?]? ??}, ??email:?null? } ......
文本索引
文本索引將建立索引的文檔字段先進行分詞再進行檢索,但是目前還不支持中文分詞.
下面增加兩個文本字段,創建一個聯合文本索引
db.blog.insertMany([ ??{title:"hello?world",content:"mongodb?is?the?best?database"}, ??{title:"index",content:"efficient?data?structure"} ]) //創建索引 db.blog.createIndex({title:"text",content:"text"}) 'title_text_content_text' //使用文本索引查詢 db.blog.find({$text:{$search:"hello?data"}}) {? ??_id:?ObjectId("61c092268c4037d17827d977"), ??title:?'index', ??content:?'efficient?data?structure'? }, {? ??_id:?ObjectId("61c092268c4037d17827d976"), ??title:?'hello?world', ??content:?'mongodb?is?the?best?database'? }
唯一索引
? ? ? ? 唯一索引就是在建立索引地字段上不能出現重復元素,除了單字段唯一索引還有聯合唯一索引以及數組唯一索引(即數組之間不能有元素交集 )
//對title字段創建唯一索引 db.blog.createIndex({title:1},{unique:true}) 'title_1' //插入一個已經存在的title值 db.blog.insertOne({title:"hello?world",content:"mongodb?is?the?best?database"}) MongoServerError:?E11000?duplicate?key?error?collection:?mock.blog?index:?title_1?dup?key:?{?:?"hello?world"?} //查看一下執行計劃,isUnique為true db.blog.find({"title":"index"}).explain() {? ??queryPlanner:? ??{? ?????...... ?????winningPlan:? ?????{? ????????stage:?'FETCH', ????????inputStage:? ????????{? ???????????stage:?'IXSCAN', ???????????keyPattern:?{?title:?1?}, ???????????indexName:?'title_1', ???????????isMultiKey:?false, ???????????multiKeyPaths:?{?title:?[]?}, ???????????isUnique:?true, ???????????...... ?????????}? ?????}, ?????rejectedPlans:?[]? ??}, ??....... ??ok:?1? }
相關視頻教程推薦:《MongoDB教程》