1. 程式人生 > >MongoDB數據庫索引

MongoDB數據庫索引

lai 不包含 dup 占用 當前 integer title 就是 oca

索引通常能夠極大的提高查詢的效率,如果沒有索引,MongoDB在讀取數據時必須掃描集合中的每個文件並選取那些符合查詢條件的記錄。這種掃描全集合的查詢效率是非常低的,特別在處理大量的數據時,查詢可以要花費幾十秒甚至幾分鐘,這對網站的性能是非常致命的。本文將詳細介紹MongoDB數據庫索引

引入

  索引能夠提高查詢效率,如何體現呢?接下來使用性能分析函數explain()來進行分析說明

  首先,插入10萬條數據

技術分享圖片

  接著,不創建索引,來尋找time範圍在100和200之間的文檔

技術分享圖片

  由圖中所知,totalDocsExamined值為100000,表示查找了100000個文檔;nReturned值為101,表示返回了101個文檔;executionTimeMillis值為39,表示花費了39ms

  下面,我們在time字段上建立索引

技術分享圖片

  再次,尋找time範圍在100和200之間的文檔

技術分享圖片

  由圖可知,totalDocsExamined和nReturned值都是101,executionTimeMillis值為0,相當於從101個文檔中,找到了101個文檔,查找的速度趨近於0。由此可見,使用索引極大地提升了查詢速度

概述

  索引是特殊的數據結構,以易於遍歷的形式存儲數據集的一小部分。 索引存儲特定字段或一組字段的值,按照索引中指定的字段值排序

  使用索引,可以加快索引相關的查詢,也相應地帶來一些壞處

  1、增加磁盤空間的消耗。在索引比較多的情況下,索引文件所占據的空間有可能超過數據本身

  2、在寫入數據或更新數據時,對索引的維護一般是寫之外的另一條邏輯,一定程度上,會降低寫入性能

  但是,為了查詢的高效,這些影響是值得的。有很多情況下,系統的性能下降,與不合理的索引創建有關。所以,合理的創建索引,可以減少索引帶來的不好的影響

索引設置

【getIndexes()】

  使用getIndexes()方法來查詢索引

db.collection_name.getIndexes()

  由下圖可知,有"_id"和"time"兩個索引

技術分享圖片

【createIndex()】

db.COLLECTION_NAME.createIndex({KEY:1})

  語法中Key值為要創建的索引字段,1為指定按升序創建索引,如果想按降序來創建索引指定為-1即可

技術分享圖片 

  當然,也可以創建多個索引字段

db.COLLECTION_NAME.createIndex({k1:1,k2:1})

  在MongoDB3.0版本之前,使用的是ensureIndex()方法,現在ensureIndex()方法依然可以使用,只是createIndex()方法的別名

  如果文檔較多,創建索引需要耗費一定的時間。如果系統負載較重,且有很多已經存在的文檔,不能直接使用這個命令進行創建,需要在使用數據庫之前,就將索引創建完畢。否則,嚴重影響數據庫的性能

  [註意]索引可以重復創建,如果對已經存在的索引再次創建,會直接返回成功

技術分享圖片

  createIndex() 接收可選參數,可選參數列表如下:

技術分享圖片

Parameter     Type      Description
background    Boolean    建索引過程會阻塞其它數據庫操作,background可指定以後臺方式創建索引,默認值為false
unique        Boolean    建立的索引是否唯一。指定為true創建唯一索引。默認值為false
name          string     索引的名稱。如果未指定,MongoDB通過連接索引的字段名和排序順序生成一個索引名稱
dropDups      Boolean    在建立唯一索引時是否刪除重復記錄,指定 true 創建唯一索引。默認值為 falsesparse        Boolean    對文檔中不存在的字段數據不啟用索引;如果設置為true,索引字段中不會查詢出不包含對應字段的文檔。默認值為falsev             index version    索引的版本號。默認的索引版本取決於mongod創建索引時運行的版本
weights       document   索引權重值,數值在 1 到 99,999 之間,表示該索引相對於其他索引字段的得分權重
expireAfterSeconds  integer   指定一個以秒為單位的數值,完成 TTL設定,設定集合的生存時間
default_language    string    對於文本索引,該參數決定了停用詞及詞幹和詞器的規則的列表。 默認為英語
language_override   string    對於文本索引,該參數指定了包含在文檔中的字段名,默認值為language

技術分享圖片

db.db_coll1.createIndex({time:1},{background:true})

【dropIndex()】

  使用db.collection_name.dropIndex({key:1})方法可以刪除指定索引

db.collection_name.dropIndex({key:1})

技術分享圖片

  [註意]_id索引無法被刪除

技術分享圖片

  除了使用鍵值對來刪除索引,還可以使用其name值來刪除索引

  如下所示,{time:1}的name值為"time_1",使用db.db_coll1.dropIndex("time_1")也可以刪除索引

技術分享圖片

【dropIndexes()】

  使用db.collection_name.dropIndexes()方法可以刪除所有索引

db.collection_name.dropIndexes()

技術分享圖片

索引屬性

【TTL】

  過期索引又稱為TTL索引,是一種特殊類型的單字段索引,主要用於當滿足某個特定時間之後自動刪除相應的文檔。也就是說集合中的文檔有一定的有效期,超過有效期的文檔就會失效,會被移除。也即是數據會過期。過期的數據無需保留,這種情形適用於如機器生成的事件數據,日誌和會話信息等等

  同樣地,過期索引使用createIndex()方法來創建,但它支持第二個參數expireAfterSeconds,用來指定多少秒過期或者包含過期日期值的數組

 db.eventlog.createIndex( { x: 1 }, { expireAfterSeconds: 3600 } )

  以下示例中,在60s後,會刪除time文檔

技術分享圖片

  使用過期索引,有以下幾點註意事項

  1、存儲在過期索引字段的值必須是指定的時間類型。必須是ISODate或者ISODate數組,不能使用時間戳,否則不能被自動刪除

  以下示例中time設置了ISODate類型的值,該值到60s後會被自動刪除

技術分享圖片

  以下示例中,time設置了時間戳,該值到60s後無法被刪除

技術分享圖片

  2、如果指定了ISODate時間數組,則按照最小的時間進行刪除

  3、過期索引不能是復合索引

  4、刪除時間是不精確的。刪除過程是由後臺程序每60s跑一次,而且刪除也需要一些時間,存在誤差。所以,如果設置的到期時間與當前時間的間隔小於60s,則文檔最少也要60s才能被刪除

【唯一性】

  索引的屬性可以具有唯一性,即唯一索引,只要設置索引屬性中的unique為true即可,默認為false。唯一索引用於確保索引字段不存儲重復的值,即強制索引字段的唯一性。缺省情況下,mongodb的_id字段在創建集合的時候會自動創建一個唯一索引

db.collection_name.createIndex({},{unique:true})

  如下圖所示,在默認情況下,不是唯一索引

技術分享圖片

  在設置unique:true後,不能插入重復的值

技術分享圖片

常見錯誤

  如下圖所示,設置的a字段為唯一索引,b字段也無法輸入重復的值。這是因為設置a字段為唯一索引,插入數據b:10,相當於a:null,再插入b:10時,相當於又插入了a:null。而a:null和a:null是重復的,而a字段是唯一索引,無法重復。所以,無法插入重復的b:10

技術分享圖片

【稀疏性】

  索引的屬性可以具有稀疏性,即稀疏索引,只要設置索引屬性中的sparse為true即可,默認為false

db.collection_name.createIndex({},{sparse:true})

  稀疏性的不同代表了MongoDB在處理索引中存在但文檔中不存在的字段的兩種不同的方法

  稀疏索引,也稱為間隙索引就是創建索引的索引列在某些文檔上列不存在,導致索引存在間隙。

  假設,在一個集合中,創建了x字段上的索引。但是,插入的文檔中並不包含x字段。在默認情況下,MongoDB依然會為這條不存在的字段創建索引。如果把這條索引創建為稀疏索引,則這條索引將不會被使用

  如果數據集合中很多文檔在創建索引的字段上並沒有值,使用稀疏索引可以減少磁盤占用,且提高插入速度

$exits

  使用稀疏索引時,可能會帶來一些隱患。MongoDB提供了一種$exits操作符,$exits表示字段是否存在

技術分享圖片

  由下圖所示,創建了{m:1}的稀疏索引,使用find()方法查找不存在m字段的文檔時,結果出現了。是因為,MongoDB並沒有使用稀疏索引來查詢

  如果使用hint()方法強制使用稀疏索引來查找索引上存在而文檔中不存在的字段,則沒有結果。再次說明稀疏索引不能用來查找索引上存在,但文檔裏不存在的字段

技術分享圖片

索引種類

  MongoDB支持基於集合文檔上任意列創建索引。缺省情況下,所有的文檔的_id列上都存在一個索引。基於業務的需要,可以基於一些重要的查詢和操作來創建一些額外的索引。這些索引可以是單列,也可是多列(復合索引),多鍵索引,地理空間索引,全文索引等

  MongoDB支持6種索引,包括

  1、_id索引

  2、單鍵索引

  3、多鍵索引

  4、復合索引

  5、全文索引

  6、地理位置索引

【_id索引】

  _id索引是絕大多數集合默認建立的索引,對於每個插入的數據,MongDB都會自動生成一條唯一的_id字段

  由下圖所示,在未插入任何索引之前,已經存在_id索引

技術分享圖片

【單鍵索引】

  單鍵索引是最普通的索引,與_id索引不同,單鍵索引不會自動創建

  比如,一條記錄,形式為{x:1,y:2,z:3},在x字段上建立索引,之後就可以使用x為條件進行查詢

技術分享圖片

【多鍵索引】

  在MongoDB中可以基於數組來創建索引。mongodb為數組每一個元素創建索引值。多鍵索引支持數組字段的高效查詢。多鍵索引能夠基於字符串,數字數組以及嵌套文檔進行創建

  多鍵索引與單鍵索引創建形式相同,區別在於字段的值。單鍵索引的值為單一的值,如一個字符串、數字或日期。多鍵索引的值具有多個記錄,如一個數組

  如果mongoDB中插入數組類型的多鍵數據,索引是自動建立的,無需刻意指定。但是,使用getIndexes()方法並沒有多鍵索引,除非顯式地創建多鍵索引

【復合索引】

  MongoDB支持復合索引,即將多個鍵組合到一起創建索引。該方式稱為復合索引,或者也叫組合索引,該方式能夠滿足多鍵值匹配查詢使用索引的情形。其次復合索引在使用的時候,也可以通過前綴法來使用索引

  [註意]任意復合索引字段不能超過31個

  比如,插入{x:1,y:2,z:3}的記錄,當需要按照x與y的值進行查詢時,就需要創建x與y的復合索引。接著,就可以使用x和y作為條件進行查詢

技術分享圖片

db.db_coll1.createIndex({x:1,y:1})
db.db_coll1.createIndex({x:-1,y:1})
db.db_coll1.createIndex({x:-1,y:-1})
db.db_coll1.createIndex({x:1,y:-1})
db.db_coll1.createIndex({y:1,x:1})
db.db_coll1.createIndex({y:-1,x:1})
db.db_coll1.createIndex({y:-1,x:-1})
db.db_coll1.createIndex({y:1,x:-1})

技術分享圖片

  復合索引創建時按升序或降序來指定其排列方式。對於單鍵索引,其順序並不是特別重要,因為MongoDB可以在任一方向遍歷索引。對於復合索引,按何種方式排序能夠決定該索引在查詢中能否被使用到

  x與y的復合索引共包括以上8種情況,x和y的先後次序不同,升序或降序不同 ,都會產生不同的索引。而查詢優化器,會使用我們建立的這些索引來創建查詢方案,最終選擇出最優的索引來查詢數據

  索引前綴指的是復合索引的子集

  假如存在如下索引

{ "item": 1, "location": 1, "stock": 1 }

  那存在下列索引前綴

{ item: 1 }
{ item: 1, location: 1 }

  在MongoDB中,下列查詢過濾條件情形中,索引將會被使用到

item字段
item字段 + location字段
item字段 + location字段 + stock字段
item字段 + stock字段(盡管索引被使用,但不高效)

  以下過濾條件查詢情形,索引將不會被使用到

location字段
stock字段
location + stock字段

全文索引

【創建】

  全文索引也叫做文本索引,常見於搜索框中。我們在搜索框中輸入關鍵詞,比如 "HTML",不僅標題中帶有"HTML"的文章會被搜索出來,而且文章中存在"HTML"的文章也會被搜索出來

  為了索引一個存儲字符串或者字符串數組的鍵,需要在創建選項中包含這個鍵並指定為 "text" ,如下:

db.reviews.createIndex( { comments: "text" } )

  如果需要在多個字段上創建全文索引,則可以復合索引

技術分享圖片

db.reviews.createIndex(
   {
     subject: "text",
     comments: "text"
   }
 )

技術分享圖片

  如果需要對所有字段創建全文索引,則需要使用$xx標識

db.collection_name.createIndex( { "$**": "text" } )

  [註意]一個集合最多只能創建 一個 文本 索引

【使用】

  如果使用全文索引進行搜索,則需要使用如下格式

db.collection_name.find({$text:{$search: ‘...‘}})

  假設使用如下的數據結構來存儲一個完整的文章,author存儲作者,title存儲標題,article存儲文章內容

{author:"",title:"",article:""}

  現在來添加一些數據,並對所有字段創建全文索引

技術分享圖片

  下面來搜索‘huochai‘,可搜索到3條記錄

技術分享圖片

  如果搜索‘a2‘,則只能搜索到第2條記錄

技術分享圖片

  如果搜索‘a1 a2 a3‘,則相當於或的關系,a1 或 a2 或 a3,可以搜索到3條記錄

技術分享圖片

  如果搜索‘huochai -css‘,相當於查找包含‘huochai‘,但不包含‘css‘的記錄,包括第1和第3條

技術分享圖片

  如果要搜索且關系,比如同時包含huochai和css的記錄,則需要在內部添加引號," \"huochai\" \"css\" "

  [註意]只支持雙引號

技術分享圖片

【相似度】

  全文索引有一個相似度的概念,表示全文索引的搜索條件與記錄的內容有多麽相似

  在find()方法的第二個參數中,score是一個數字,該數字越大,表示相似度越高

db.collection_name.find({$text:{$search: ‘...‘}},{score:{$meta:"textScore"}})

  現在,再插入一條內容,作者為‘huochai‘

技術分享圖片

  然後開始搜索‘huochai‘,並帶有相似度

技術分享圖片

  下面使用相似度排序,相似度高的排在前面

sort({score:{$meta:"textScore"}})

技術分享圖片

【限制】

  1、每次查詢,只能指定一個$text查詢

  2、$text查詢不能出現在$nor查詢中

  3、查詢中如果包含了$text,hint()將不再起作用

  4、只能對整個單詞查詢,不能對單詞的截取部分查詢。類似地,中文做全文查詢的時候,只能查詢一段話中有空格的該字或者詞

地理位置索引

  一般地,地理位置索引可以實現諸如按距離排序的餐館、在某區域內的店鋪篩選等

  可以將一些點的位置存儲在MongoDB中,創建地理位置索引索引後,可以按照位置來查找其他點。地理位置索引可以分為兩種:一種是2d索引,用於存儲和查找平面上的點;另一種是2dsphere索引,用於存儲和查找球面上的點

  查找方式一般有兩種:一種是查找距離某個點一定範圍內的點;另一種是查找包含在某區域內的點

【2D索引】

  2D索引的創建方式如下

db.<collection>.createIndex( { <location field> : "2d" , <additional field> : <value> } , { <index-specification options> } )

  options包括如下參數

{ min : <lower bound> , max : <upper bound> , bits : <bit precision> }

技術分享圖片

  在mongodb中使用經緯度表示位置,[經度, 緯度]。經度範圍在[-180,180],緯度範圍在[-90,90]

  [註意]默認邊界允許插入大於90或小於-90的不合理緯度值的文檔。而對於這樣不合理的點的地理查詢,數據庫行為是不可預知的。所以,盡量避免插入超出範圍的維度值

技術分享圖片

  2D索引的查詢方式有三種,包括$near、$geoNear、$geoWithin

  一種是使用$near查詢,即查詢距離某個點最近的點,默認返回100個

db.<collection>.find( { <location field> :{ $near : [ <x> , <y> ] } } )

技術分享圖片

  $maxDistance可以設置離當前點最遠的距離

技術分享圖片

  $minDistance可以設置離當前點最近的距離

技術分享圖片

  另一種是使用$geoNear查詢,$geoNear使用runCommand命令進行使用

db.runCommand({geoNear:<collection_name>,near:[x,y],minDistance:..,maxDistance:..,num:...})

技術分享圖片

  另一種是使用$geoWithin查詢,即查詢某個形狀內的點

  在mongodb中,有三種形狀,包括矩形、圓形和多邊形,使用方法如下

db.<collection>.find( { <location field> :{ $geoWithin :{ $box|$polygon|$center : <coordinates>} } } )

  第一種是矩形,使用$box表示

{$box:[[x1,y1],[x2,y2]]}

技術分享圖片

  第二種是圓形,使用$center表示

{$center:[[<x1>,<y1>],r]}

技術分享圖片

  第三種是多邊形,使用$polygon表示

{$polygon:[[<x1>,<y1>],[<x2>,<y2>],[<x3>,<y3>],...]}

技術分享圖片

【2dsphere索引】

  2dsphere索引的創建方式如下

db.collection_name.createIndex({a:"2dsphere"})

  位置表示方式不再是簡單的經緯度,而是一種GeoJSON的表示方式,用來描述一個點、一條直線、多邊形等形狀,格式如下

{type:"",coordinates:[<coordinates>]}

MongoDB數據庫索引