MongoDB之索引

本文详细介绍了MongoDB中的索引类型,如唯一索引、稀疏索引和多键索引,以及如何创建、查询和删除索引。同时讨论了在大数据集背景下,如何在后台创建索引,离线节点构建索引,以及应对索引碎片化的解决方案。通过索引优化,可以显著提高查询效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MONGODB索引:索引的使用通常涉及到查询效率的问题;合理的使用索引能够非常明显的提高查询的速度;如果没有索引,在mongodb中要进行集合内所有文件的扫描查询,这种效率是非常低效的,尤其是在处理大数据集时,查询可能等待非常长的时间;这对程序而已是不可忍受的。mongdb中的索引采用了B树的数据结构进行遍历查询;这种结构在其他数据库中是很常见的;mongodb中索引类型有很多中,像唯一索引,稀疏索引和多键索引。

mongodb创建索引模版

db.collection.createIndex(keys,options); //mongodb 版本version3.0后推荐使用
db.collection.ensureIndex(keys,options); //mongodb版本version3.0后不建议使用

 

索引参数说明
参数名称类型说明
keysdocument

包含字段和值对的文档,其中字段是索引键,值描述该字段的索引类型。对于字段的升序索引,请指定值1; 对于降序索引,请指定值-1。 MongoDB支持几种不同的索引类型,包括 文本,地理空间和散列索引。有关更多信息,请参阅索引类型 从3.6开始,您不能指定*为索引名称。

optionsdocument可选的。包含一组控制索引创建的选项的文档。有关详情,请参阅下面options的表格说明
options参数说明
参数名称类型说明
backgroundboolean可选的。构建在后台创建索引,所以操作不会影响或阻止其他数据库活动。指定true在后台构建。默认值为false。(集合中有大量数据时,最好指定为true,否则会对插入和修改造成影响)
uniqueboolean

可选的。创建唯一索引,以便集合不接受索引键值与索引中现有值匹配的文档的插入或更新。

指定true以创建唯一索引。默认值为false

该选项不适用于散列索引。

namestring

可选的。索引的名称。如果未指定,MongoDB通过连接索引字段的名称和排序顺序来生成索引名称。

无论是用户指定还是MongoDB生成,索引名称包括其完整命名空间(即database.collection)都不能长于mongodb索引名称指定长度 

partialFilterExpressiondocument

可选的。如果指定,则索引仅引用与筛选表达式匹配的文档。有关详细信息,请参阅部分索引

过滤器表达式可以包括:

您可以partialFilterExpression为所有MongoDB 索引类型指定选项 。

版本3.2中的新功能。

sparseboolean

可选的。如果true,索引仅引用具有指定字段的文档。这些索引使用较少的空间,但在某些情况下(特别是排序)表现不同。默认值为false。有关详细信息,请参阅稀疏索引

在3.2版中更改:从MongoDB 3.2开始,MongoDB提供了创建部分索引的选项 。部分索引提供了稀疏索引功能的超集。如果您使用的是MongoDB 3.2或更高版本,则部分索引应优先于稀疏索引。

在2.6版中更改:默认情况下2dsphere索引是稀疏的并忽略此选项。对于包含2dsphere索引键和其他类型键的复合索引 ,只有 2dsphere索引字段确定索引是否引用文档。

2d geoHaystack和 文本索引的行为类似于 2dsphere索引。

expireAfterSecondsinteger可选的。指定一个值(以秒为单位)作为TTL来控制MongoDB在此集合中保留文档的时间。有关此功能的更多信息,请参阅 通过设置TTL从集合中过期数据。这仅适用于TTL索引。
storageEnginedocument

可选的。允许用户在创建索引时基于每个索引配置存储引擎。

storageEngine选项应采用以下形式:

复制

复制

storageEngine : {  < 存储- 引擎- 名称>: < 选项>  }
创建索引时指定的存储引擎配置选项在复制期间进行验证并记录到oplog,以支持具有使用不同存储引擎的成员的副本集。
3.0版中的新功能。

 

  1. 唯一索引:创建唯一性索引,设置unique为true即可;确保数据唯一性
    db.users.createIndex({userName:1},{unique:true}); //db.users.ensureIndex({userName:1},{unique:true});
  2. 稀疏索引(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}); //评论建立稀疏索引

     

  3. 多键索引(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")
explain参数说明
参数名称类型说明
verbositystring

可选的。指定解释输出的详细模式。该模式影响行为explain()并确定要返回的信息量。可能的模式有:"queryPlanner", "executionStats",和"allPlansExecution"

默认模式是"queryPlanner"

与早期版本的向后兼容性 cursor.explain(),MongoDB的解释true作为"allPlansExecution"false作为"queryPlanner"

有关模式的更多信息,请参阅 详细模式

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
}
结果集参数说明
参数名称类型说明
queryPlannerdocument包含查询优化器选择查询计划的信息
queryPlanner.namespacestring指定<database>.<collection>运行查询的命名空间
queryPlanner.indexFilterSetboolean指定MongoDB是否为查询形状应用了索引过滤器
queryPlanner.winningPlandocument查询优化程序选择的计划的文档。MongoDB将计划呈现为阶段树; 即一个阶段可以有一个 inputStage或者,如果该阶段有多个子阶段, inputStages
queryPlanner.winningPlan.stagestring

表示阶段名称的字符串。

每个阶段都包含特定于阶段的信息。例如,一个IXSCAN阶段将包括索引边界以及特定于索​​引扫描的其他数据。如果该阶段具有子阶段或多个子阶段,则该阶段将具有inputStage或inputStage

queryPlanner.winningPlan.inputStagestring描述子阶段的文档,它为其父级提供文档或索引键。如果父级只有一个子级,该字段存在
queryPlanner.winningPlan.inputStagesdocument描述子阶段的一系列文档。子阶段为父阶段提供文档或索引键。如果父级具有多个子节点,该字段存在。例如,$或表达式索引交集的阶段消耗来自多个源的输入
queryPlanner.rejectedPlansdocument查询优化器考虑和拒绝的候选计划数组。如果没有其他候选计划,则该数组可以为空。
executionStatsdocument描述获胜计划的已完成查询执行的统计信息。对于写操作,完成查询执行指的是修改被执行,但并没有修改应用到数据库。
executionStats.nReturnedinteger与查询条件匹配的文档数。 nReturned对应于早期版本的MongoDB n返回的字段cursor.explain()
executionStats.executionTimeMillisinteger查询计划选择和查询执行所需的总时间(以毫秒为单位)。executionTimeMillis对应于早期版本的MongoDB millis返回的字段cursor.explain()
executionStats.totalKeysExaminedinteger扫描的索引条目数。 totalKeysExamined对应于早期版本的MongoDB nscanned返回的 字段cursor.explain()
executionStats.totalDocsExaminedinteger查询执行期间检查的文档数。检查文档的常见查询执行阶段是COLLSCAN 和FETCH
executionStats.executionStagesdocument

将获胜计划的完成执行细节作为阶段树; 即阶段可以有一个inputStage或多个 inputStages

每个阶段都包含特定于阶段的执行信息。

executionStats.executionStages.worksinteger指定查询执行阶段执行的“工作单元”的数量。查询执行将其工作分为小单元。“工作单位”可能包括检查单个索引键,从集合中提取单个文档,将投影应用于单个文档或执行内部簿记。
executionStats.executionStages.advancedinteger此阶段返回或提前的中间结果数到其父阶段。
executionStats.executionStages.needTimeinteger未将中间结果推进到其父级的工作循环数(请参阅参考资料explain.executionStats.executionStages.advanced)。例如,索引扫描阶段可以花费一个工作周期来寻找索引中的新位置而不是返回索引关键字; 这个工作周期将计入explain.executionStats.executionStages.needTime而不是explain.executionStats.executionStages.advanced
executionStats.executionStages.needYieldinteger存储层请求查询阶段暂停处理并产生锁定的次数。
executionStats.executionStages.saveStateinteger查询阶段暂停处理并保存其当前执行状态的次数,例如准备产生锁定。
executionStats.executionStages.restoreStateinteger查询阶段恢复已保存的执行状态的次数,例如在恢复先前已生成的锁之后。
executionStats.executionStages.isEOFinteger|boolean
  • 如果true1,则执行阶段已到达流末尾。
  • 如果false0,舞台可能仍然有结果返回。例如,考虑具有限制的查询,其执行阶段包含LIMIT具有IXSCAN查询输入阶段的阶段。如果查询返回的值超过指定的限制,则该LIMIT阶段将报告,但其基础阶段将报告。isEOF: 1IXSCANisEOF: 0
executionStats.executionStages.inputStage.keysExaminedinteger

对于扫描索引(例如IXSCAN)的查询执行阶段, keysExamined是在索引扫描过程中检查的入站和越界键的总数。如果索引扫描由单个连续范围的键组成,则只需要检查入站键。如果索引边界由若干键范围组成,则索引扫描执行过程可以检查越界键,以便从一个范围的末尾跳到下一个范围的开头。

请考虑以下示例,其中有一个字段索引, x集合包含100个x值为1到100的文档:db.keys.find( { x : { $in : [ 3, 4, 50, 74, 75, 90 ] } } ).explain( "executionStats" );

该查询将扫描键34。然后它将扫描密钥 5,检测到它是否超出范围,并跳到下一个密钥50

继续这一过程中,查询扫描键3,4,5,50,51,74,75,76,90,和91键 55176,和91被外的边界键仍在审查。值为keysExamined是10。

executionStats.executionStages.inputStage.docsExaminedinteger

指定在查询执行阶段扫描的文档数。

COLLSCAN舞台呈现,以及从集合中检索文档的阶段(例如FETCH

executionStats.executionStages.inputStage.seeksinteger

版本3.4中的新功能:仅适用于索引扫描(IXSCAN)阶段。

我们必须寻找索引光标到新位置才能完成索引扫描的次数。

executionStats.allPlansExecution 包含在计划选择阶段为获胜和被拒绝的计划捕获的部分执行信息 。仅当以详细模式运行时,该字段才存在 。explainallPlansExecution
serverInfo
document对于未加密的集合,explain返回serverInfo
serverInfo.hoststring主机信息
serverInfo.portinteger端口
serverInfo.versionstring版本信息
serverInfo.gitVersionstringgit版本信息

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值