数据库索引与书籍的索引类似,有了索引就不需要翻整本书,数据库可以直接在索引中查找,在索引中找到条目后,就可以直接跳到目标文档的位置,这可以让查找的速度提高几个数量级。

一、创建索引

我们在person这个集合的age键上创建一个索引,比较一下创建索引前后,一个查询的语句的性能区别。

创建索引:db.person.ensureIndex({"age":1})。这里我们使用了ensureIndex在age上建立了索引。“1”:表示按照age进行升序,“-1”:表示按照age进行降序。

没有索引的查询性能:

有索引的查询性能:

我们主要来看这几个参数,(参数说明,请看上一篇文章)

executionTimeMillis(这次query整体的耗时):无索引耗时毫秒 ;有索引耗时毫秒。

totalDocsExamined(文档扫描条目):无索引是200万条;有索引是条。

stage(查询的类型):无索引是COLLSCAN(全表扫描);有索引是FETCH+IXSCAN(索引扫描+根据索引去检索指定document)。

executionStages.executionTimeMillisEstimate(检索document获得数据的耗时):无索引耗时毫秒;有索引耗时毫秒。

建好索引后,这个query整体的速度提高了1个数量级 (1个数量级是10倍的意思)。根据查询语句的不同,索引可以使速度提高几个数量级。

二、复合索引 

在多个键上建立的索引就是复合索引,有时候我们的查询不是单条件的,可能是多条件,比如查找年龄在20~30名字叫‘ryan1’的同学,那么我们可以建立“age”和“name”的联合索引来加速查询。

为了演示索引的效果,我们来重新生成插入一份200万个文档的集合。

 //删除原来的集合
db.person.drop(); //插入200万条数据
for(var i=0;i<2000000;i++){
db.person.insert({"name":"ryan"+i%1000,"age":20+i%10});
} //创建三个索引
db.person.ensureIndex({"age":1})
db.person.ensureIndex({"name":1,"age":1})
db.person.ensureIndex({"age":1,"name":1})

我们可以用hint()方法来强制查询走哪个索引。

我们来看一下,当查询条件是多个的时候,复合索引相比单键索引的强大魅力。

db.person.find({"age":{"$gte":20,"$lte":30},"name":"ryan1"}).hint({"age":1}).explain("executionStats");

 {
...
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 2000,
"executionTimeMillis" : 2031,
"totalKeysExamined" : 2000000,
"totalDocsExamined" : 2000000,
...
}

db.person.find({"age":{"$gte":20,"$lte":30},"name":"ryan1"}).hint({"age":1,"name":1}).explain("executionStats");

 {
...
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 2000,
"executionTimeMillis" : 8,
"totalKeysExamined" : 2010,
"totalDocsExamined" : 2000,
...
}

从executionTimeMillis的值上,一眼就可以看出却别。单间索引耗费了2031毫秒,复合索引用了8毫秒。 由此我们可以看出,根据查询语句的不同,建立正确的索引是非常重要的,对于查询语句中是多条件的,应多考虑复合索引的应用。

下面,我们再说一种复合索引的重要应用情况。有对一个键排序并只要前100个结果的情景(实际项目中经常都是这种情景)。对于这种情况,索引应该这样建{"sortKey":1,"queryCriteria":1},排序的键应该放在复合索引的第一位。

db.person.find({"age":{"$gte":21.0,"$lte":30.0}}).sort({"name":1}).limit(100).hint({"age":1,"name":1}).explain("executionStats");

 {
...
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 100,
"executionTimeMillis" : 6882,
"totalKeysExamined" : 1800000,
"totalDocsExamined" : 1800000,
...
}

db.person.find({"age":{"$gte":21.0,"$lte":30.0}}).sort({"name":1}).limit(100).hint({"name":1,"age":1}).explain("executionStats");

 {
...
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 100,
"executionTimeMillis" : 3,
"totalKeysExamined" : 2100,
"totalDocsExamined" : 2100,
...
}

从上面的结果,我们很容易看出,基于排序键的索引,效果非常好。

分析:第一种索引,需要找到所有复合查询条件的值(依据索引,键和文档可以快速找到),但是找到后,需要对文档在内存中进行排序,这个步骤消耗了非常多的时间。第二种索引,效果非常好,因为不需要在内存中对大量数据进行排序。但是,MongoDB不得不扫描整个索引以便找到所有文档。因此,如果对查询结果的范围做了限制,那么MongoDB在几次匹配之后就可以不再扫描索引,在这种情况下,将排序键放在第一位是一个非常好的策略。

三、唯一索引

唯一索引可以确保集合的每个文档的指定键都有唯一值。如果想保证不同文档的“name”键拥有不同的值,在“name”键上创建一个唯一索引就可以了。

db.person.ensureIndex({"name":1},{"unique":true});

然后用db.person.getIndexes()命令,查看目前person集合所有的索引。

也可以创建复合的唯一索引。创建复合唯一索引时,单个键的值可以相同,但所有键的组合值必须是唯一的。

db.person.ensureIndex({"name":1,"age":1},{"unique":true});

四、稀疏索引

唯一索引会把null看作值,所以无法将多个缺少唯一索引中的键的文档插入到集合中。然而,在有些情况下,你可能希望唯一索引只对包含相应键的文档生效。这个时候我们可以用到MongoDB中的稀疏索引。该索引与关系型数据库中的稀疏索引是完全不同的概念。MongoDB中的稀疏索引只是不需要将每个文档都作为索引条目。

比如,如果有一个可选的mobilephone字段,但是,如果提供了这个字段,那么它的值必须是唯一的:

db.person.ensureIndex({"mobilephone":1}{"unique":true,"sparse":true});

稀疏索引不必是唯一的。只要去掉unique选项,就可以创建一个非唯一的稀疏索引。

五、索引管理

如第一小节所述,可以使用ensureIndex方法创建新的索引,也可以使用createIndex方法。

创建一个索引之后,可以利用getIndexes()方法来查看给定集合上的所有索引的信息。

db.person.getIndexes();得到的结果如下所示。

 [
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "personmap.person"
},
{
"v" : 1,
"unique" : true,
"key" : {
"name" : 1.0
},
"name" : "name_1",
"ns" : "personmap.person"
},
{
"v" : 1,
"unique" : true,
"key" : {
"name" : 1.0,
"age" : 1.0
},
"name" : "name_1_age_1",
"ns" : "personmap.person"
},
{
"v" : 1,
"unique" : true,
"key" : {
"mobilephone" : 1.0
},
"name" : "mobilephone_1",
"ns" : "personmap.person",
"sparse" : true
}
]

db.person.getIndexes()方法的结果

随着业务的不断变化,你可能会发现数据或者查询已经发生了改变,原来的索引也不那么好用了。这时可以使用dropIndex()方法删除不需要的索引:

db.person.dropIndex("name_1");//删除索引名为name_1的索引。

  喜欢请微信扫描下面二维码,关注我公众号--“精修Java”,做一些实战项目中的问题和解决方案分享。

玩转mongodb(六):索引,速度的引领(普通索引篇)的更多相关文章

  1. 玩转mongodb(七):索引,速度的引领(全文索引、地理空间索引)

    本篇博文主要介绍MongoDB中一些常用的特殊索引类型,主要包括: 用于简单字符串搜索的全文本索引: 用于球体空间(2dsphere)和二维平面(2d)的地理空间索引. 一.全文索引 MongoDB有 ...

  2. 玩转mongodb(九):通过log4jmongo来实现分布式系统的日志统一管理

    背景 在分布式系统中,我们有多个web app,这些web app可能分别部署在不同的物理服务器上,并且有各自的日志输出.当生产问题来临时,很多时候都需要去各个日志文件中查找可能的异常,相当耗费人力. ...

  3. 玩转mongodb(五):mongodb 3.0+ 查询性能分析

    mongodb性能分析方法:explain() 为了演示的效果,我们先来创建一个有200万个文档的记录.(我自己的电脑耗了15分钟左右插入完成.如果你想插更多的文档也没问题,只要有耐心等就可以了.) ...

  4. MongoDB学习笔记,基础+增删改查+索引+聚合...

    一 基础了解 对应关系 -> https://docs.mongodb.com/manual/reference/sql-comparison/ database -> database ...

  5. 【Microsoft Azure 的1024种玩法】六、使用Azure Cloud Shell对Linux VirtualMachines 进行生命周期管理

    [文章简介] Azure Cloud Shell 是一个用于管理 Azure 资源的.可通过浏览器访问的交互式经验证 shell. 它使用户能够灵活选择最适合自己工作方式的 shell 体验,本篇文章 ...

  6. MongoDB之几种情况下的索引选择策略

    一.MongoDB如何选择索引 如果我们在Collection建了5个index,那么当我们查询的时候,MongoDB会根据查询语句的筛选条件.sort排序等来定位可以使用的index作为候选索引:然 ...

  7. MongoDB的备份和部署 高级功能索引,聚合复制,分片

    创建备份 MongoDB 数据转储 为了在 MongoDB 中创建数据库备份,需要使用 mongodump 命令.该命令会将服务器上的所有数据都转储到 dump 目录中.你可以使用很多选项来限制转储的 ...

  8. IDEA 出现 updating indices 卡进度条问题的解决方案并加快索引速度

    缺点: 这样的话,前端的接口(也就是字符串)就搜索不到了. C:\Users\Administrator\.IntelliJIdea2017.3\system  删除里面的caches文件夹(这里的 ...

  9. MongoDB快速入门(十二) -- 索引

    MongoDB 索引 索引支持的解析度的查询效率.如果没有索引,MongoDB必须扫描每一个文档的集合,要选择那些文档相匹配的查询语句.这种扫描的效率非常低,会要求 mongod 做大数据量的处理. ...

随机推荐

  1. A River Runs Through It

           Our birth is but a sleep and a forgetting:   The Soul that rises with us, our life's Star,    ...

  2. Android-自定义ListView下拉刷新与上拉加载

    效果图: 第一步:编写需要在ListView中增加头加载的布局文件,与底部加载的布局文件: 头布局文件: <?xml version="1.0" encoding=" ...

  3. zookeeper集群崩溃处理

    今天在私有化项目中遇到如下问题: 1.客户反馈用户登录返回303 2.登录服务器查看是大量的log将服务器磁盘空间占用殆尽,导致所有服务进程仍旧存在但是监听端口失败,服务不可用 3.清理日志文件 4. ...

  4. python 应用 base64、hmac、hashlib包实现:MD5编码 base64编码解码、SHA256编码、urlsafe_b64encode编码等等基本所有的加密签名的方法

    用python做HTTP接口自动化测试的时候,接口的很多参数是经过各种编码加密处理后在传到后台的,这里列举出python实现 应用 base64.hmac.hashlib包实现:md5编码 sha1编 ...

  5. .net core grpc consul 实现服务注册 服务发现 负载均衡(二)

    在上一篇 .net core grpc 实现通信(一) 中,我们实现的grpc通信在.net core中的可行性,但要在微服务中真正使用,还缺少 服务注册,服务发现及负载均衡等,本篇我们将在 .net ...

  6. 在ASP.NET MVC中使用区域来方便管理controller和view

    在ASP.NET MVC中使用区域来方便管理controller和view 在mvc架构中,一般在controllers和views中写所有控制器和视图, 太多控制器时候,为了方便管理,想要将关于pe ...

  7. Spring+Mybatis整合时 Failed to read candidate component class,Caused by:IllegalArgumentException

    Spring+Mybatis整合时Caused by: java.lang.IllegalArgumentException错误 org.springframework.beans.factory.B ...

  8. Optimizing Your App for Today’s Internet

    这个 session 的主讲人感觉是一个很典型的美国人,年纪也不小. 网络现状 四十亿人在使用因特网,大概占有世界人口的一半.上网人数的增长在减缓. 但是网络仍然在增长.增长点主要在物联网.第三世界国 ...

  9. linux磁盘用满的两种情况

    首先查看分区的内存使用情况和inode使用情况 第一种情况,内存用完: 我们拿分区sdb5来做实验 查看内存使用情况和inode使用情况: 第二种情况:inode使用完 我们在该分区下创建很多空文件 ...

  10. struts2中s:select标签的使用

    1.第一个例子: <s:select list="{'aa','bb','cc'}" theme="simple" headerKey="00& ...