MONGODB索引:索引的使用通常涉及到查询效率的问题;合理的使用索引能够非常明显的提高查询的速度;如果没有索引,在mongodb中要进行集合内所有文件的扫描查询,这种效率是非常低效的,尤其是在处理大数据集时,查询可能等待非常长的时间;这对程序而已是不可忍受的。mongdb中的索引采用了B树的数据结构进行遍历查询;这种结构在其他数据库中是很常见的;mongodb中索引类型有很多中,像唯一索引,稀疏索引和多键索引。
mongodb创建索引模版
db.collection.createIndex(keys,options); //mongodb 版本version3.0后推荐使用
db.collection.ensureIndex(keys,options); //mongodb版本version3.0后不建议使用
参数名称 | 类型 | 说明 |
---|---|---|
keys | document | 包含字段和值对的文档,其中字段是索引键,值描述该字段的索引类型。对于字段的升序索引,请指定值1; 对于降序索引,请指定值-1。 MongoDB支持几种不同的索引类型,包括 文本,地理空间和散列索引。有关更多信息,请参阅索引类型 从3.6开始,您不能指定*为索引名称。 |
options | document | 可选的。包含一组控制索引创建的选项的文档。有关详情,请参阅下面options的表格说明 |
参数名称 | 类型 | 说明 |
---|---|---|
background | boolean | 可选的。构建在后台创建索引,所以操作不会影响或阻止其他数据库活动。指定true 在后台构建。默认值为false 。(集合中有大量数据时,最好指定为true,否则会对插入和修改造成影响) |
unique | boolean | 可选的。创建唯一索引,以便集合不接受索引键值与索引中现有值匹配的文档的插入或更新。 指定 该选项不适用于散列索引。 |
name | string | 可选的。索引的名称。如果未指定,MongoDB通过连接索引字段的名称和排序顺序来生成索引名称。 无论是用户指定还是MongoDB生成,索引名称包括其完整命名空间(即 |
partialFilterExpression | document | 可选的。如果指定,则索引仅引用与筛选表达式匹配的文档。有关详细信息,请参阅部分索引。 过滤器表达式可以包括: 您可以 版本3.2中的新功能。 |
sparse | boolean | 可选的。如果 在3.2版中更改:从MongoDB 3.2开始,MongoDB提供了创建部分索引的选项 。部分索引提供了稀疏索引功能的超集。如果您使用的是MongoDB 3.2或更高版本,则部分索引应优先于稀疏索引。 在2.6版中更改:默认情况下2dsphere索引是稀疏的并忽略此选项。对于包含 2d, geoHaystack和 文本索引的行为类似于 2dsphere索引。 |
expireAfterSeconds | integer | 可选的。指定一个值(以秒为单位)作为TTL来控制MongoDB在此集合中保留文档的时间。有关此功能的更多信息,请参阅 通过设置TTL从集合中过期数据。这仅适用于TTL索引。 |
storageEngine | document | 可选的。允许用户在创建索引时基于每个索引配置存储引擎。 该 |
- 唯一索引:创建唯一性索引,设置unique为true即可;确保数据唯一性
db.users.createIndex({userName:1},{unique:true}); //db.users.ensureIndex({userName:1},{unique:true});
- 稀疏索引(sparse index):索引默认都是密集型的。但有些情况使用密集型索引并不合适; 一种情况是对并未出现在集合里的字段进行唯一索引时。如注册用户与绑定的手机号码,确保手机号唯一;初始创建用户还未绑定手机号phone,phone为null的项了,这种情况密集型索引就不合适了,就需要使用稀疏索引。
db.users.find({phone:null});//对用户手机号进行查询,手机号唯一 //创建稀疏索引 db.users.createIndex({phone:1},{unique:true,sparse:true});
另一种情况是集合中有大量文档不包含被索引键。例如:某些网站可以进行匿名评论(即不登录就评论信息),对于不存在用户的ID值,如果用户ID值有索引,而评论信息集合中大部分没有用户ID,这种情况下效率就会很差;第一增加了索引的大小。第二在增加和删除带null值的用户ID字段的文档时,也要求更新索引。如果很少或不会对匿名的评论进行查询,那么可以选择建立一个对用户ID的稀疏索引。
db.reviews.createIndex({userId:1},{sparse:true}); //评论建立稀疏索引
-
多键索引(multikey index):它允许索引中有多个条目指向相同的文档。如:用户的爱好,可能有很多,这个字段是一个数组,如果我们在favorite字段上定义索引,能够对这个字段上的数组任一值的查询用索引来定位文档。
//{userId:1,favorite:['football','novel','climb','tea']} db.users.createIndex({favorite:1});//创建年龄和部门的多键索引
索引的查询: db.collections.getIndexes();或者db.collections.getIndexSpecs();
db.getCollection('collectcontents').getIndexSpecs()
/* 输出结果 */
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "data_collect.collectcontents"
},
{
"v" : 2,
"key" : {
"time" : 1.0
},
"name" : "time_1",
"ns" : "data_collect.collectcontents"
}
]
索引的删除: db.collections.dropIndex('name');
db.getCollection('test_user').dropIndex('addresss_1'); //通过索引名称进行删除
/* 结果集 */
{
"nIndexesWas" : 2,
"ok" : 1.0
}
索引的构建的注意事项:
大多时候创建索引是把程序投入正式使用之前添加索引,这种情况是允许集合随着数据的插入增量的构建索引。
但是还是有特例情况,进行相反的操作过程。第一种是在切换到正式环境之前需要导入大批量数据。如:你想要把数据库数据迁移到mongodb ,需从数据仓库导入数据。你可以事先在导入数据之前创建索引,但在导入数据之后创建索引能从一开始就保证理想的平衡性和密集的索引,也能将构建索引的净时间降到最低。
第二种情况就是发生在对某个查询进行优化的时候,这种事显而易见的。
创建索引的过程,对于大数据集合,有时候在构建索引时,花费的时间可能是几个小时甚至几天。可以通过mongodb的日志监控索引的构建过程。
索引的解决方案:
1、后台索引:在正式环境,如果暂停对数据库的访问是无法忍受的,可以指定后台创建索引。这个操作虽然会占据写锁,但构建任务会暂停让其他读写操作访问数据库。如果应用程序中大量使用mongodb,后台索引会降低性能,在一些使用场合还是可以接受的。如应用程序访问流量在某个时间段内最低,可以在这个时间段了完成索引的创建。设置backgroud:true
db.users.createIndex({phone:1},{backgroud:true});
2、离线索引:如果正式环境数据集合太大,无法再短时间内完成索引的创建,这时候需其他方案。通常这会涉及到一个副本节点的下线,然后在下线节点上创建索引,随后让其上的数据与主节点同步。一旦完成数据同步,把该节点提升为主节点,再让另一个节点下线,构建它自己的索引。该策略假设你的赋值oplog够大,能够避免离线节点的数据在索引构建过程中变得陈旧。
3、备份:索引的创建很难时,你可能会希望对数据进行备份,可惜并非所有备份方法都包含索引。如你可能使用mongodump和mongorestore,但这些工具仅保存了集合和索引声明。当运行mongorestore时,所备份的所有集合上声明的索引都会重新创建一遍。如果大数据集合,那么构建时间也会很长,让人无法忍受。
4、压紧:如果应用程序会有大量数据的更新和删除,其结构会导致在构建索引过程中,索引的碎片化程度很高。虽然B树数据结构会自己合并,但这样的操作不能抵消大批量删除修改的影响。碎片过多的索引大小远超对指定数据集大小的预期,也会让索引使用更多的内存。这种情况下,可能希望你重建一个或多个索引,可以删除并重新创建单个索引,或者运行reIndex命令(它会重建指定集合上的所有索引;重建过程中要注意,该命令会占用写锁,让你的mongodb实例无法使用。重建最好是在线下操作。
db.collections.reIndex();
索引查询优化:
explain函数命令
db.products.explain("queryPlanner|executionStats|allPlansExecution")
参数名称 | 类型 | 说明 |
---|---|---|
verbosity | string | 可选的。指定解释输出的详细模式。该模式影响行为 默认模式是 与早期版本的向后兼容性 有关模式的更多信息,请参阅 详细模式。 |
db.getCollection('collectcontents').find({action:'011'}).sort({time:1}).explain();
//结果集
/* 1 */
{
"queryPlanner" : { //queryPlanner查询优化器选择查询计划的信息
"plannerVersion" : 1,
"namespace" : "data_collect.collectcontents", //一个字符串,指定<database>.<collection>运行查询的命名空间。
"indexFilterSet" : false,
"parsedQuery" : {
"action" : {
"$eq" : "011"
}
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"action" : {
"$eq" : "011"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"time" : 1.0
},
"indexName" : "time_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"time" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"time" : [
"[MinKey, MaxKey]"
]
}
}
},
"rejectedPlans" : []
},
"serverInfo" : {
"host" : "localhost.localdomain",
"port" : 40001,
"version" : "3.4.0",
"gitVersion" : "f4240c60f005be757399042dc12f6addbc3170c1"
},
"ok" : 1.0
}
参数名称 | 类型 | 说明 |
---|---|---|
queryPlanner | document | 包含查询优化器选择查询计划的信息 |
queryPlanner.namespace | string | 指定<database>.<collection> 运行查询的命名空间 |
queryPlanner.indexFilterSet | boolean | 指定MongoDB是否为查询形状应用了索引过滤器。 |
queryPlanner.winningPlan | document | 查询优化程序选择的计划的文档。MongoDB将计划呈现为阶段树; 即一个阶段可以有一个 inputStage 或者,如果该阶段有多个子阶段, inputStages |
queryPlanner.winningPlan.stage | string | 表示阶段名称的字符串。 每个阶段都包含特定于阶段的信息。例如,一个 |
queryPlanner.winningPlan.inputStage | string | 描述子阶段的文档,它为其父级提供文档或索引键。如果父级只有一个子级,则该字段存在 |
queryPlanner.winningPlan.inputStages | document | 描述子阶段的一系列文档。子阶段为父阶段提供文档或索引键。如果父级具有多个子节点,则该字段存在。例如,$或表达式或索引交集的阶段消耗来自多个源的输入 |
queryPlanner.rejectedPlans | document | 查询优化器考虑和拒绝的候选计划数组。如果没有其他候选计划,则该数组可以为空。 |
executionStats | document | 描述获胜计划的已完成查询执行的统计信息。对于写操作,完成查询执行指的是修改将被执行,但并没有修改应用到数据库。 |
executionStats.nReturned | integer | 与查询条件匹配的文档数。 nReturned 对应于早期版本的MongoDB n 返回的字段cursor.explain() |
executionStats.executionTimeMillis | integer | 查询计划选择和查询执行所需的总时间(以毫秒为单位)。executionTimeMillis 对应于早期版本的MongoDB millis 返回的字段cursor.explain() 。 |
executionStats.totalKeysExamined | integer | 扫描的索引条目数。 totalKeysExamined 对应于早期版本的MongoDB nscanned 返回的 字段cursor.explain() 。 |
executionStats.totalDocsExamined | integer | 查询执行期间检查的文档数。检查文档的常见查询执行阶段是COLLSCAN 和FETCH 。 |
executionStats.executionStages | document | 将获胜计划的完成执行细节作为阶段树; 即阶段可以有一个 每个阶段都包含特定于阶段的执行信息。 |
executionStats.executionStages.works | integer | 指定查询执行阶段执行的“工作单元”的数量。查询执行将其工作分为小单元。“工作单位”可能包括检查单个索引键,从集合中提取单个文档,将投影应用于单个文档或执行内部簿记。 |
executionStats.executionStages.advanced | integer | 此阶段返回或提前的中间结果数到其父阶段。 |
executionStats.executionStages.needTime | integer | 未将中间结果推进到其父级的工作循环数(请参阅参考资料explain.executionStats.executionStages.advanced )。例如,索引扫描阶段可以花费一个工作周期来寻找索引中的新位置而不是返回索引关键字; 这个工作周期将计入explain.executionStats.executionStages.needTime 而不是explain.executionStats.executionStages.advanced 。 |
executionStats.executionStages.needYield | integer | 存储层请求查询阶段暂停处理并产生锁定的次数。 |
executionStats.executionStages.saveState | integer | 查询阶段暂停处理并保存其当前执行状态的次数,例如准备产生锁定。 |
executionStats.executionStages.restoreState | integer | 查询阶段恢复已保存的执行状态的次数,例如在恢复先前已生成的锁之后。 |
executionStats.executionStages.isEOF | integer|boolean |
|
executionStats.executionStages.inputStage.keysExamined | integer | 对于扫描索引(例如IXSCAN)的查询执行阶段, 请考虑以下示例,其中有一个字段索引, 该查询将扫描键 继续这一过程中,查询扫描键3,4,5,50,51,74,75,76,90,和91键 |
executionStats.executionStages.inputStage.docsExamined | integer | 指定在查询执行阶段扫描的文档数。 为 |
executionStats.executionStages.inputStage.seeks | integer | 版本3.4中的新功能:仅适用于索引扫描( 我们必须寻找索引光标到新位置才能完成索引扫描的次数。 |
executionStats.allPlansExecution | 包含在计划选择阶段为获胜和被拒绝的计划捕获的部分执行信息 。仅当以详细模式运行时,该字段才存在 。explain allPlansExecution | |
serverInfo | document | 对于未加密的集合,explain 返回serverInfo |
serverInfo.host | string | 主机信息 |
serverInfo.port | integer | 端口 |
serverInfo.version | string | 版本信息 |
serverInfo.gitVersion | string | git版本信息 |