索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中( 索引存储在特定字段或字段集的值),并且是使用了B-tree结构。索引能够极大程度提高MongoDB查询效率。
若是没有索引,MongoDB必须执行全集合collections扫描,即扫描集合中的每一个文档,选取符合查询条件的文档document。 若是查询时存在适当的索引,MongoDB可使用索引来限制它必须查询的文档document的数量,特别是在处理大量数据时,因此选择正确的索引是很关键的、重要的。mongodb
建立索引,须要考虑的问题:数据库
索引限制:数组
索引建立使用createIndex()方法,格式以下:服务器
db.collection.createIndex(<key and index type specification>,<options>)
复制代码
createIndex() 接收可选参数,可选参数列表以下:数据结构
Parameter | Type | Description |
---|---|---|
background | Boolean | 建索引过程会阻塞其它数据库操做,background可指定之后台方式建立索引,即增长 "background" 可选参数。 "background" 默认值为false。 |
unique | Boolean | 创建的索引是否惟一。指定为true建立惟一索引。默认值为false. |
name | string | 索引的名称。若是未指定,MongoDB的经过链接索引的字段名和排序顺序生成一个索引名称。 |
dropDups | Boolean | 3.0+版本已废弃。在创建惟一索引时是否删除重复记录,指定 true 建立惟一索引。默认值为 false. |
sparse | Boolean | 对文档中不存在的字段数据不启用索引;这个参数须要特别注意,若是设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false. |
expireAfterSeconds | integer | 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。 |
v | index version | 索引的版本号。默认的索引版本取决于mongod建立索引时运行的版本。 |
weights | document | 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其余索引字段的得分权重。 |
default_language | string | 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语 |
language_override | string | 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language. |
查看Collection中全部索引,格式以下:ide
db.collection.getIndexes()
复制代码
删除Collection中的索引:格式以下:函数
db.collection.dropIndexes() //删除全部索引
db.collection.dropIndex() //删除指定的索引
复制代码
索引的默认名称是索引键和索引中每一个键的value1或-1,形式index_name+1/-1,好比:性能
db.products.createIndex( { item: 1, quantity: -1 } )----索引名称为item_1_quantity_-1
复制代码
也能够指定索引名称:学习
db.products.createIndex( { item: 1, quantity: -1 } , { name: "inventory" } ) ----索引名称为inventory
复制代码
方法 | 解析 |
---|---|
db.currentOp() | 查看索引建立过程 |
db.killOp(opid) | 终止索引建立,其中-opid为操做id |
形式 | 解析 |
---|---|
$indexStats | 获取索引访问信息 |
explain() | 返回查询状况:在executionStats模式下使用db.collection.explain()或cursor.explain()方法返回有关查询过程的统计信息,包括使用的索引,扫描的文档数以及查询处理的时间(以毫秒为单位)。 |
Hint() | 控制索引,例如要强制MongoDB使用特定索引进行db.collection.find()操做,请使用hint()方法指定索引 |
MongoDB提供了许多索引使用和操做的度量标准,在分析数据库的索引使用时可能须要考虑这些度量标准,以下所示:优化
形式 | 解析 |
---|---|
metrics.queryExecutor.scanned | 在查询和查询计划评估期间扫描的索引项的总数 |
metrics.operation.scanAndOrder | 返回没法使用索引执行排序操做的已排序数字的查询总数 |
collStats.totalIndexSize | 全部索引的总大小。 scale参数会影响此值。若是索引使用前缀压缩(这是WiredTiger的默认值),则返回的大小将反映计算总计时任何此类索引的压缩大小。 |
collStats.indexSizes | 指定集合collection上每一个现有索引的键和大小。 scale参数会影响此值 |
dbStats.indexes | 包含数据库中全部集合的索引总数的计数。 |
dbStats.indexSize | 在此数据库上建立的全部索引的总大小 |
在密集(快达到数据库最大容量)Collection建立索引:在默认状况下,在密集的Collection(快达到数据库最大容量)时建立索引,会阻止其余操做。在给密集的Collection(快达到数据库最大容量)建立索引时, 索引构建完成以前,保存Collection的数据库不可用于读取或写入操做。 任何须要对全部数据库(例如listDatabases)进行读或写锁定的操做都将等待不是后台进程的索引构建完成。
所以可使用background属性进行设置后台索引建立,操做以下:
db.people.createIndex( { zipcode: 1 }, { background: true } )
默认状况下,在建立索引时,background为false,能够和其余属性进行组合使用:
db.people.createIndex( { zipcode: 1 }, { background: true, sparse: true } )
复制代码
MongoDB能够在任何一个字段中建立索引,默认状况下,全部的集合(collections)会在_id字段中建立索引。_id索引是为防止客户端插入具备相同value的_id字段的文档Document,并且不能删除_id字段索引。
在分片群集中使用_id索引,若是不使用_id字段做为分片键,则应用程序必须确保_id字段中值的惟一性以防止出错,解决方法为使用标准的自动生成的ObjectId来完成。
通常单字段索引的value中,“1”指定按升序对项目进行排序的索引,“-1”指定按降序对项目进行排序的索引。以下所示:
在单个字段建立索引,示例以下:
{
"_id": ObjectId("570c04a4ad233577f97dc459"),
"score": 1034,
"location": { state: "NY", city: "New York" }
}
//建立单字段索引
db.records.createIndex( { score: 1 } )
//支持的查询
db.records.find( { score: 2 } )
db.records.find( { score: { $gt: 10 } } )
复制代码
在嵌入式文档Document中的字段建立索引,示例以下:
db.records.createIndex( { "location.state": 1 } )
//支持的查询
db.records.find( { "location.state": "CA" } )
db.records.find( { "location.city": "Albany", "location.state": "NY" } )
复制代码
在嵌入式文档Document建立索引,示例以下:
db.records.createIndex( { location: 1 } )
//支持查询
db.records.find( { location: { city: "New York", state: "NY" } } )
复制代码
复合索引指的是将多个key组合到一块儿建立索引,这样能够加速匹配多个键的查询。特性以下:
建立复合索引的格式:
db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } )
复制代码
排序顺序,两个字段的复合索引示例,index{userid:1,score:-1},先userid的value排序,而后再userid排序基础下进行score排序。以下图:
建立复合索引,示例以下:
{
"_id": ObjectId(...),
"item": "Banana",
"category": ["food", "produce", "grocery"],
"location": "4th Street Store",
"stock": 4,
"type": "cases"
}
//建立复合索引
db.products.createIndex( { "item": 1, "stock": 1 } )
//支持的查询
db.products.find( { item: "Banana" } )
db.products.find( { item: "Banana", stock: { $gt: 5 } } )
复制代码
复合索引中的前缀查询,示例以下:
//建立复合索引
db.products.createIndex({ "item": 1, "location": 1, "stock": 1 })
//前缀为:{ item: 1 }与{ item: 1, location: 1 }
//支持前缀查询为
db.products.find( { item: "Banana" } )
db.products.find( { item: "Banana", location: “beijing”} )
//不支持前缀查询,不会提升查询效率
//不包含前缀字段
db.products.find( { location: “beijing”} )
db.products.find( { stock: { $gt: 5 } )
db.products.find( { location: “beijing”,stock: { $gt: 5 } )
//不按照建立复合索引字段顺序的前缀查询
db.products.find( { location: “beijing”,item: "Banana" },stock: { $gt: 5 } )
复制代码
MongoDB使用多键索引为数组的每一个元素都建立索引,多键索引能够创建在字符串、数字等key或者内嵌文档(document)的数组上,若是索引字段包含数组值,MongoDB会自动肯定是否建立多键索引; 您不须要手动指定多键类型。 其中建立方式:
db.coll.createIndex( { <field>: < 1 or -1 > } )
复制代码
索引边界
使用多键索引,会出现索引边界(索引边界便是查询过程当中索引能查找的范围)的计算,并计算必须遵循一些规则。即当多个查询的条件中字段都存在索引中时,MongoDB将会使用交集或者并集等来判断这些条件索引字段的边界最终产生一个最小的查找范围。能够分状况:
1).交集边界
交集边界即为多个边界的逻辑交集,对于给定的数组字段,假定一个查询使用了数组的多个条件字段而且可使用多键索引。若是使用了$elemMatch链接了条件字段,则MongoDB将会相交多键索引边界,示例以下:
//survey Collection中document有一个item字段和一个ratings数组字段
{ _id: 1, item: "ABC", ratings: [ 2, 9 ] }
{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }
//在ratings数组上建立多键索引:
db.survey.createIndex({ratings:1})
//两种查询
db.survey.find({ratings:{$elemMatch:{$gte:3,$lte:6}}}) //(1)
db.survey.find( { ratings : { $gte: 3, $lte: 6 } } ) //(2)
复制代码
查询条件分别为大于等于三、小于等于6,其中 (1)中使用了$elemMatch链接查询条件,会产生一个交集ratings:[[3,6]。在(2)查询中,没使用$elemMatch,则不会产生交集,只要知足任何一个条件便可。
2).并集边界
并集边界经常用在肯定多键组合索引的边界,例如:给定的组合索引{a:1,b:1},在字段a上有一个边界:[3,+∞),在字段b上有一个边界:(-∞,6],相并这两个边界的结果是:{ a: [ [ 3, Infinity ] ], b: [ [ -Infinity, 6 ] ] }。
并且若是MongoDB无法并集这两个边界,MongoDB将会强制使用索引的第一个字段的边界来进行索引扫描,在这种状况下就是: a: [ [ 3, Infinity ] ]。
三、数组字段的组合索引
一个组合索引的索引字段是数组,例如一个survey collection集合document文档中含有item字段和ratings数组字段,示例以下:
{ _id: 1, item: "ABC", ratings: [ 2, 9 ] }
{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }
//在item字段和ratings字段建立一个组合索引:
db.survey.createIndex( { item: 1, ratings: 1 } )
//查询条件索引包含的两个key
db.survey.find( { item: "XYZ", ratings: { $gte: 3 } } )
复制代码
分别处理查询条件:
item: "XYZ" --> [ [ "XYZ", "XYZ" ] ];
ratings: { $gte: 3 } --> [ [ 3, Infinity ] ].
复制代码
MongoDB使用并集边界来组合这两个边界:
{ item: [ [ "XYZ", "XYZ" ] ], ratings: [ [ 3, Infinity ] ] }
复制代码
4).内嵌文档document的数组上创建组合索引
若是数组中包含内嵌文档document,想在包含的内嵌文档document字段上创建索引,须要在索引声明中使用逗号“,” 来分隔字段名,示例以下:
ratings: [ { score: 2, by: "mn" }, { score: 9, by: "anon" } ]
复制代码
则score字段名称就是:ratings.score。
5).混合不是数组类型的字段和数组类型字段的并集
{ _id: 1, item: "ABC", ratings: [ { score: 2, by: "mn" }, { score: 9, by: "anon" } ] }
{ _id: 2, item: "XYZ", ratings: [ { score: 5, by: "anon" }, { score: 7, by: "wv" } ] }
//在item和数组字段ratings.score和ratings.by上建立一个组合索引
db.survey2.createIndex( { "item": 1, "ratings.score": 1, "ratings.by": 1 } )
//查询
db.survey2.find( { item: "XYZ", "ratings.score": { $lte: 5 }, "ratings.by": "anon" } )
复制代码
分别对查询条件进行处理:
item: "XYZ"--> [ ["XYZ","XYZ"] ];
score: {$lte:5}--> [[-Infinity,5]];
by: "anon" -->["anon","anon"].
复制代码
MongoDB能够组合 item键的边界与 ratings.score和ratings.by两个边界中的一个,究竟是score仍是by索引边界这取决于查询条件和索引键的值。MongoDB不能确保哪一个边界和item字段进行并集。 但若是想组合ratings.score和ratings.by边界,则查询必须使用$elemMatch。
6).数组字段索引的并集边界
在数组内部并集索引键的边界,
好比:在ratings.score和ratings.by字段上建立组合索引:
db.survey2.createIndex( { "ratings.score": 1, "ratings.by": 1 } )
复制代码
字段ratings.score和ratings.by拥有共同的路径ratings。下面的查询使用$elemMatch则要求ratings字段必须包含一个元素匹配这两个条件:
db.survey2.find( { ratings: { $elemMatch: { score: { $lte: 5 }, by: "anon" } } } )
复制代码
分别对查询条件进行处理:
score: { $lte: 5 } --> [ -Infinity, 5 ];
by: "anon"--> [ "anon", "anon" ].
复制代码
MongoDB可使用并集边界来组合这两个边界:
{ "ratings.score" : [ [ -Infinity, 5 ] ], "ratings.by" : [ [ "anon", "anon" ] ] }
复制代码
7). 还有不使用$elemMatch进行查询以及不完整的路径上使用$elemMatch,想要了解更多能够查看《官方文档-Multikey Index Bounds》。
限制:
MongoDB提供了一种全文索引类型,支持在Collection中搜索字符串内容,对字符串与字符串数组建立全文可搜索的索引 。 这些全文索引不存储特定于语言的停用词(例如“the”,“a”,“或”),而且阻止document集合中的单词仅存储根词。建立方式以下:
db.collection.createIndex( { key: "text",key:"text" ..... } )
复制代码
并且MongoDB提供权重以及通配符的建立方式。查询方式多个字符串空格隔开,排除查询使用“-”以下所示:
db.collection.find({$text:{$search:"runoob add -cc"}})
复制代码
要删除全本索引,须要将索引的名称传递给db.collection.dropIndex()方法, 而要获取索引的名称,使用db.collection.getIndexes()方法。
还能够指定全文索引的语言,经过default_language属性 在建立时指定, 或者使用language_override属性 覆盖掉建立document文档时默认的语言,以下所示:
//指定不一样的语言的方法:建立全文索引的时候使用default_language属性
db.collection.createIndex(
{ content : "text" },
{ default_language: "spanish" })
//使用language_override属性覆盖默认的语言
db.quotes.createIndex( { quote : "text" },
{ language_override: "idioma" } )
//默认的全文索引名称为context_text,users.comments.text,指定名称MyTextIndex
db.collection.createIndex(
{
content: "text",
"users.comments": "text",
"users.profiles": "text"
},
{
name: "MyTextIndex"
}
)
复制代码
权重
每一个全文索引能够经过设置权重来分配不一样的搜索程度,默认权重为1,对于文档中的每一个索引字段,MongoDB将匹配数乘以权重并将结果相加。 使用此总和,MongoDB而后计算文档的分数,示例以下:
{
_id: 1,
content: "This morning I had a cup of coffee.",
about: "beverage",
keywords: [ "coffee" ]
}
{
_id: 2,
content: "Who doesn't like cake?",
about: "food",
keywords: [ "cake", "food", "dessert" ]
}
//经过db.blog.createIndex来指定weight权重
db.blog.createIndex(
{
content: "text",
keywords: "text",
about: "text"
},
{
weights: {
content: 10,
keywords: 5
},
name: "TextIndex"
}
)
复制代码
content权重为10,keywords为5,about为默认权重1,所以能够得出content对于keywords查询频率高于2倍,而对于about字段则是10倍。
通配符全文索引
在多个字段上建立全文索引时,还可使用通配符说明符($**)。 使用通配符全文索引,MongoDB会为包含Collection中每一个Document的字符串数据。例如:
db.collection.createIndex( { "$**": "text" } )
复制代码
通配符全本索引是多个字段上的全本索引。 所以,能够在建立索引期间为特定字段指定权重,以控制结果的排名。
限制
散列索引使用散列函数来计算索引字段值的散列值。 散列函数会折叠嵌入的文档并计算整个值的散列值,但不支持多键(即数组)索引。 生成hash索引key使用了convertShardKeyToHashed()方法。建立方式以下:
db.collection.createIndex( { _id: "hashed" } )
复制代码
并且散列索引支持使用散列分片键进行分片。 基于散列的分片使用字段的散列索引做为分片键来分割整个分片群集中的数据。
索引属性有TTL索引、唯一性索引、部分索引、稀疏索引以及区分大小写索引。
TTL索引是特殊的单字段索引,而且字段类型必须是date类型或者包含有date类型的数组,MongoDB可使用它在必定时间后或在特定时钟时间自动从集合中删除文档。 数据到期对于某些类型的信息很是有用,例如机器生成的事件数据,日志和会话信息,这些信息只须要在数据库中持续有限的时间。
建立TTL索引方法,和普通索引的建立方法同样,只是会多加一个expireAfterSeconds的属性,格式以下:
db.collection.createIndex( {key and index type specification},{ expireAfterSeconds: time})
复制代码
例子:
db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } )
复制代码
指定过时时间
首先在保存BSON日期类型值或BSON日期类型对象数组的字段上建立TTL索引,并指定expireAfterSeconds值为0.对于集合中的每一个文档,设置 索引日期字段为与文档到期时间对应的值。示例操做以下:
第一步:
db.log_events.createIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } )
复制代码
第二步:
db.log_events.insert( {
"expireAt": new Date('July 22, 2013 14:00:00'),
"logEvent": 2,
"logMessage": "Success!"
} )
复制代码
数据过时类型:
TTL索引特有限制:
惟一索引可确保索引字段不存储重复值; 即强制索引字段的惟一性。 默认状况下,MongoDB在建立集合期间在_id字段上建立惟一索引。建立方式以下:
db.collection.createIndex( <key and index type specification>, { unique: true } )
复制代码
单个字段建立方式,示例以下:
db.members.createIndex( { "user_id": 1 }, { unique: true } )
复制代码
惟一性复合索引: 还能够对复合索引强制执行惟一约束。 若是对复合索引使用惟一约束,则MongoDB将对索引键值的组合强制实施惟一性。示例以下:
//建立的索引且强制groupNumber,lastname和firstname值组合的惟一性。
db.members.createIndex( { groupNumber: 1, lastname: 1, firstname: 1 }, { unique: true } )
复制代码
惟一多键索引:
{ _id: 1, a: [ { loc: "A", qty: 5 }, { qty: 10 } ] }
//建立索引:
db.collection.createIndex( { "a.loc": 1, "a.qty": 1 }, { unique: true } )
//插入数据:惟一索引容许将如下Document插入Collection中,由于索引强制执行a.loc和a.qty值组合的惟一性:
db.collection.insert( { _id: 2, a: [ { loc: "A" }, { qty: 5 } ] } )
db.collection.insert( { _id: 3, a: [ { loc: "A", qty: 10 } ] } )
复制代码
建立惟一索引到副本或者分片中: 对于副本集和分片集群,使用滚动过程建立惟一索引须要在过程当中中止对集合的全部写入。 若是在过程当中没法中止对集合的全部写入,请不要使用滚动过程。 相反,经过如下方式在集合上构建惟一索引:
NOTE:详细解析能够看
限制:
部分索引经过指定的过滤表达式去达到局部搜索。经过db.collection.createIndex()方法中增长partialFilterExpression属性建立,过滤表达式以下:
示例以下:
//建立部分索引
db.restaurants.createIndex(
{ cuisine: 1, name: 1 },
{ partialFilterExpression: { rating: { $gt: 5 } } }
)
//查询状况分类
db.restaurants.find( { cuisine: "Italian", rating: { $gte: 8 } } ) //(1)
db.restaurants.find( { cuisine: "Italian", rating: { $lt: 8 } } ) //(2)
db.restaurants.find( { cuisine: "Italian" } ) //(3)
复制代码
其中:
限制:
稀疏索只引搜索包含有索引字段的文档的条目,跳过索引键不存在的文档,即稀疏索引不会搜索不包含稀疏索引的文档。默认状况下, 2dsphere (version 2), 2d, geoHaystack, 全文索引等老是稀疏索引。建立方式db.collection.createIndex()方法增长sparse属性,以下所示:
db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } )
复制代码
稀疏索引不被使用的状况: 若是稀疏索引会致使查询和排序操做的结果集不完整,MongoDB将不会使用该索引,除非hint()示显式指定索引。
稀疏复合索引:
稀疏索引与惟一性: 一个既包含稀疏又包含惟一的索引避免集合上存在一些重复值得文档,可是容许多个文档忽略该键。知足稀疏索引和惟一性操做其两个限制都要遵循。
整合示例以下:
{ "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" }
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
//在score中建立稀疏索引:
db.scores.createIndex( { score: 1 } , { sparse: true } )
//查询
db.scores.find( { score: { $lt: 90 } } ) //(1)
db.scores.find().sort( { score: -1 } ) //(2)
db.scores.find().sort( { score: -1 } ).hint( { score: 1 } ) //(3)
//在score字段上建立具备惟一约束和稀疏过滤器的索引:
db.scores.createIndex( { score: 1 } , { sparse: true, unique: true } )
//该索引容许插入具备score字段的惟一值的文档或不包括得分字段的文档。以下:
db.scores.insert( { "userid": "AAAAAAA", "score": 43 } )
db.scores.insert( { "userid": "BBBBBBB", "score": 34 } )
db.scores.insert( { "userid": "CCCCCCC" } )
db.scores.insert( { "userid": "DDDDDDD" } )
//索引不容许添加如下文档,由于已存在score值为82和90的文档
db.scores.insert( { "userid": "AAAAAAA", "score": 82 } )
db.scores.insert( { "userid": "BBBBBBB", "score": 90 } )
复制代码
其中:
索引策略:
后续还会有MongonDB索引优化,副本集以及分片总结、最重要还会总结在使用MongoDB实战以及实战过程出现的一些坑,可关注后续更新MongoDB系列。
最后可关注公众号,一块儿学习,天天会分享干货,还有学习视频干货领取!