[1]引入 [2]概述 [3]索引設置 [4]索引種類 [5]全文索引 [6]地理位置索引 ...
前面的話
索引通常能夠極大的提高查詢的效率,如果沒有索引,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 創建唯一索引。預設值為 false sparse Boolean 對文檔中不存在的欄位數據不啟用索引;如果設置為true,索引欄位中不會查詢出不包含對應欄位的文檔。預設值為false v 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>]}