百度百科上对于索引的定义大概是这样的:在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文档并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。

总结一下:索引是特殊的数据结构,存储在一个易于遍历读取的数据集合中(system.index),是对集合中一列或多列的值进行排序的一种结构。

MongoDB的索引与传统关系型数据库的索引几乎是一模一样,索引的结构都是B树,同样符合最左前缀匹配原则,都是用来提升查询效率的。

前面的文章我们曾说过,每个数据库里有一个保留的集合:system.index,这个集合就是索引的存放位置,用来存放当前数据库库下所有的索引。通过db.system.index查看数据库里的所有索引。

db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.blog" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.article" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.student" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.version" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.ydm" }

上面是我环境test数据库下的所有索引,大家看到除了主键索引,没有其他索引了。下面我们会以该数据库为基准,进行索引的创建、修改、删除。

一、索引的分类

1、主键索引

对于每个集合而言,都有一个_id主键索引,一般情况下,一个集合只会有一个主键索引。

2、唯一索引

唯一索引用来标识该集合下唯一索引的字段值只能有一个,主键索引也是唯一索引。

3、一般索引

在某个列上创建的索引

4、稀疏索引

5、全文索引

有关索引的具体分类信息请参考:MongoDB索引的种类

二、索引的创建

通过createIndex来完成索引的创建,索引创建的时候可以设置是否为唯一索引、是否在后台创建、组合索引键的排序方式等选项。

索引的创建有两种时机:(1)刚建立集合,集合中的文档数据为空 (2)已经存在文档数据

对于第二种建立时机,会出现索引创建失败的情况,下面会举例说明。

OK,我们通过例子来实践一下。

1、一般索引

db.ydm.createIndex({"name":1,'sex':1})
{"createdCollectionAutomatically" : false,"numIndexesBefore" : 1,"numIndexesAfter" : 2,"ok" : 1
}
> db.ydm.getIndexes()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.ydm" }
{ "v" : 1, "key" : { "name" : 1, "sex" : 1 }, "name" : "name_1_sex_1", "ns" : "test.ydm" }

上面的例子建立了一个{"name":1,"sex":1}的组合索引,这里的1可以理解为升序,-1理解为降序。MongoDB通过设置的-1/1来组织数据。另外,创建索引时如果没有指定索引名字,系统会通过连接索引的字段名和排序顺序生成一个索引名称。索引的名字有大小的限制:不能超过127个字节。对于较复杂的索引,强烈建议自定义索引的名字。

db.ydm.createIndex({"name":1,'sex':1},{'name':'demo'})

2、唯一索引

创建唯一索引较一般索引再加一个选项即可:{'unique':true}

db.ydm.find()
{ "_id" : ObjectId("5b4056fcd3c72e6c8d190cf8"), "name" : "lily", "sex" : 1 }
{ "_id" : ObjectId("5b4056fcd3c72e6c8d190cf9"), "name" : "xiaoming", "sex" : 0 }
{ "_id" : ObjectId("5b40578ad3c72e6c8d190cfa"), "name" : "xiaohong", "sex" : 0, "address" : "beijing", "phone" : "187*****" }
{ "_id" : ObjectId("5b4057ccd3c72e6c8d190cfb"), "name" : "xiaohong", "sex" : 0, "address" : "beijing", "phone" : "187*****" }
{ "_id" : ObjectId("5b4057dbd3c72e6c8d190cfc"), "name" : "xiaohong", "sex" : 0, "address" : "beijing", "phone" : "187*****" }
> db.ydm.createIndex({"phone":1},{'unique':true})
{"createdCollectionAutomatically" : false,"numIndexesBefore" : 2,"errmsg" : "exception: E11000 duplicate key error index: test.ydm.$phone_1 dup key: { : null }","code" : 11000,"ok" : 0
}

上面我们创建唯一索引出现了错误,如果大家仔细观察数据库里原有数据会发现,第一二条文档数据里是没有phone字段的。MongoDB对于不存在的字段会默认插入null,这样在数据库里就存在两个phone为null的文档了,而我们创建的又是唯一索引,自然也就会造成创建失败。这里我们先删除一条数据重新进行唯一索引的创建。

db.ydm.find()
{ "_id" : ObjectId("5b4056fcd3c72e6c8d190cf9"), "name" : "xiaoming", "sex" : 0 }
{ "_id" : ObjectId("5b40578ad3c72e6c8d190cfa"), "phone" : "131**" }
{ "_id" : ObjectId("5b4057ccd3c72e6c8d190cfb"), "phone" : "152**" }
{ "_id" : ObjectId("5b4057dbd3c72e6c8d190cfc"), "phone" : "183**" }
> db.ydm.createIndex({"phone":1},{'unique':true})
{"createdCollectionAutomatically" : false,"numIndexesBefore" : 2,"numIndexesAfter" : 3,"ok" : 1
}
> db.ydm.getIndexes()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.ydm" }
{ "v" : 1, "key" : { "name" : 1, "sex" : 1 }, "name" : "name_1_sex_1", "ns" : "test.ydm" }
{ "v" : 1, "unique" : true, "key" : { "phone" : 1 }, "name" : "phone_1", "ns" : "test.ydm" }

上面我们为了演示的方便是采用手动删除具有重复phone的方式。生产环境中如果是在已有文档中创建唯一索引还是有很大可能出现重复值的,这样造成失败的又要如何做呢?难道也是一条一条的删除数据吗?答案是可以采用dropDups选项保留发现的第一个文档而删除接下来有重复值的文档。具体操作如下:

db.ydm.createIndex({"phone":1},{'unique':true,'dropDups':true})

上面我们是对phone字段创建唯一索引,实际中可能对多个字段建立复合唯一索引,这时,只要组合的值是唯一的就可以,单个字段的值文档间允许重复。这里就不在展开了,下面列出createIndex()方法的可选参数。

Parameter Type Description
background Boolean 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为false
unique Boolean 建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
name string 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
dropDups Boolean 在建立唯一索引时是否删除重复记录,指定 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.

OK,上面我们已经学会了通过system.index集合查看某个数据库下所有的索引,那如果我想看某集合下的索引呢?答案是通过db.集合.getIndexes()完成

db.ydm.getIndexes()
[{"v" : 1,"key" : {"_id" : 1},"name" : "_id_","ns" : "test.ydm"},{"v" : 1,"key" : {"name" : 1,"sex" : 1},"name" : "name_1_sex_1","ns" : "test.ydm"},{"v" : 1,"unique" : true,"key" : {"phone" : 1},"name" : "phone_1","ns" : "test.ydm"}
]

三、查询分析

OK,我们上面已经创建了索引,索引是否真正起到了加速查询的作用呢?有没有工具呢?学过关系型数据库的同学对explain应该不陌生,同样,MongoDB也支持通过explain分析数据查询性能、分析是否用到了索引、扫描行数、检索出来的文档数,通过分析对查询进行优化改进。

我们通过上述创建的索引来学习一下explain的使用。

db.ydm.find({'name':'xiaoming'}).explain()
{"queryPlanner" : {"plannerVersion" : 1,"namespace" : "test.ydm","indexFilterSet" : false,"parsedQuery" : {"name" : {"$eq" : "xiaoming"}},"winningPlan" : {"stage" : "FETCH","inputStage" : {"stage" : "IXSCAN","keyPattern" : {"name" : 1,"sex" : 1},"indexName" : "name_1_sex_1","isMultiKey" : false,"direction" : "forward","indexBounds" : {"name" : ["[\"xiaoming\", \"xiaoming\"]"],"sex" : ["[MinKey, MaxKey]"]}}},"rejectedPlans" : [ ]},"serverInfo" : {"host" : "VM_0_14_centos","port" : 27017,"version" : "3.0.6","gitVersion" : "1ef45a23a4c5e3480ac919b28afcba3c615488f2"},"ok" : 1
}

这个时候有些同学可能会有疑问了:一个集合中可能有多个索引,加入针对某个查询条件有多个索引可以match,MongoDB是如何确定采用哪个索引进行query的呢?我们需要强制MongoDB采用某个索引吗?

其实,MongoDB的查询优化器非常智能,会自动替我们选择哪个索引。初次做某个查询时,查询优化器会同时尝试各种查询方案,最先完成的被确定使用,其他的则被终止掉。查询方案会被记录下来,以备日后应对相同键的查询。查询优化器会定期重试其他方案,以保证方案的最优。

MongoDB的查询优化器已经为我们最好了各种调优,是不是很NB?

四、索引的删除

随着业务的发展,现有的索引不再满足业务诉求,我们需要删除(重建)某集合下的索引。

索引的删除有两种方式:(1)删除集合(2)dropIndex/dropIndexex

第一种方式在之前的文章中有介绍,这里就不再累述了。

dropIndex()方法用于删除指定的索引,dropIndexes()方法用于删除全部的索引。下面举例说明。

db.ydm.dropIndex({'sex':1});
{ "nIndexesWas" : 4, "ok" : 1 }

如果业务变化某个索引不再起作用,一定要及时drop掉。

关于索引,有以下几点需要注意:

(1)每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。如果你很少对集合进行读取操作,建议不使用索引。

(2)索引的创建一定要建在区分度高的字段上。

(3)索引不能被以下的查询使用:正则表达式、算术运算符、where子句

(4)如果没有对没有建立索引的键进行sort,MongoDB会将进行pai所有数据提取到内存中排序,一旦集合大到不能子在内存中排序,MongoDB就会报错。

(5)集合索引个数不能超过64个,索引名称不能多于128个字符

(6)要定期check索引,响应业务变化,及时删除无效索引,重建索引。

MongoDB学习系列 -- 索引相关推荐

  1. MongoDB学习系列 -- 数据库、集合、文档的CURD

    前面一篇章节我们已经对MongoDB的基本概念有了一个大概的了解,从今天开始,我们将进行更细粒度的学习,首先就是数据库.集合.文档的CURD操作. 为了便于操作,减少学习难度,我们这里使用javasc ...

  2. mongodb学习(六)索引

    准备工作: 先插入100万条数据 for(i=0;i<=1000000;i++){db.users.insert({"i":i,"username":&q ...

  3. MongoDB学习day08--Mongoose索引、Mongoose内置方法、扩展Mongoose Model的静态方法和实例方法...

    一.Mongoose索引 索引是对数据库表中一列或多列的值进行排序的一种结构, 可以让我们查询数据库变得更快. MongoDB 的索引几乎与传统的关系型数据库一模一样, 这其中也包括一些基本的查询优化 ...

  4. MongoDB学习系列6: mongodump

    2019独角兽企业重金招聘Python工程师标准>>> 由于要做一个推荐系统,但是原有的mongodb数据库的样本太少了, 只好从远程主机1复制数据到远程主机2. 这里使用mongo ...

  5. MongoDB学习系列9:MongoDB里的若干规则

    2019独角兽企业重金招聘Python工程师标准>>> 数据库名字:规则 不能是空字符串 不得含有'' . $  /  \ \0 应该全部小写 最多64字节. ~~~ admin数据 ...

  6. Python3 学习系列 丨 博客目录索引

    整个博客有关 Python 学习目录索引,方便快捷定位查询 基础学习篇 Python3 基础学习笔记 C01[变量和简单数据类型] Python3 基础学习笔记 C02[列表] Python3 基础学 ...

  7. access 导入 txt sql语句_从零开始学习 MySQL 系列索引、视图、导入和导出

    阅读本文大概需要 8 分钟 前言上篇文章我们学习了数据库和数据表操作语句,今天我们学习下数据库索引,视图,导入和导出的知识.作为基础篇,不会涉及到关于索引和视图的高级应用和核心概念,但是基本操作大家会 ...

  8. mysql导入dat文件_从零开始学习 MySQL 系列--索引、视图、导入和导出

    前言 上篇文章我们学习了数据库和数据表操作语句,今天我们学习下数据库索引,视图,导入和导出的知识. 作为基础篇,不会涉及到关于索引和视图的高级应用和核心概念,但是基本操作大家会了解,尤其是关于索引的内 ...

  9. 二、mongodb数据库系列——聚合操作 索引操作 权限管理

    一.mongodb的聚合操作 学习目标 了解 mongodb的聚合原理 掌握 mongdb的管道命令 掌握 mongdb的表达式 1 mongodb的聚合是什么 聚合(aggregate)是基于数据处 ...

最新文章

  1. linux tomcat端口冲突解决
  2. Nginx反向代理的实战案例
  3. uve (mui/light7)写APP的使用心得(大坑);
  4. c语言输入整数要求输出字符,求C语言 将输入整数转换成字符串输出!
  5. Javascript隐式转换
  6. 分离链接法的删除操作函数
  7. 简单php修改mysql数据类型_MySQL入门很简单—MySQL数据类型
  8. echarts 柱状图(bar)
  9. 如何使用android studio,怎么学习使用Android Studio?
  10. 将XML文件数据插入到数据库中
  11. 软件蓝图设计_智能企业的设计蓝图
  12. alpine安装curl
  13. 通过脚手架安装Ant+react+umi+dva项目(一)
  14. 国产手机销量大跌,终于被迫降价抛售清理库存
  15. java记录访问次数_Java 利用监听器来实现记录用户访问网站次数(示例代码)
  16. 15.java获取当前主机ip
  17. SonarQube8.6 使用说明
  18. 网站的配色应该如何做
  19. 如何看懂HijackThis扫描日志------学习帖
  20. 探究投资家用光伏的收益率究竟如何

热门文章

  1. 郭台铭执念夏普,富士康转型梦福兮祸兮?
  2. 【EE308FZ Lab3-2-1】Sprint Plan: RISD-IoT-Group
  3. 与finally单独使用的是try
  4. python的论文图表_干货丨史上最全的论文图表基本规范
  5. 【Android面试(1),阿里巴巴安卓面试题答案
  6. 直播一对一视频直播聊天
  7. Python文件的打开和关闭
  8. UI应该怎么学?有哪些方式呢?
  9. QGIS开发(一)——环境配置
  10. Android系国产手机操作系统汇总