2.1、索引存储结构
MongoDB索引使用B树数据结构,MySQL是B+Tree,本节内容来源于
2.1.1 B Tree(平衡二叉树)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p5YKJuFo-1601305239556)(/Users/cs/Library/Application Support/typora-user-images/image-20200917145908786.png)]
MongoDB索引使用B树数据结构,MySQL是B+Tree
每个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3指针指向的子树的数据范围为大于35。
模拟查找关键字29的过程:
- 根据根节点找到磁盘块1,读入内存。【磁盘I/O操作第1次】
- 比较关键字29在区间(17,35),找到磁盘块1的指针P2。
- 根据P2指针找到磁盘块3,读入内存。【磁盘I/O操作第2次】
- 比较关键字29在区间(26,30),找到磁盘块3的指针P2。
- 根据P2指针找到磁盘块8,读入内存。【磁盘I/O操作第3次】
- 在磁盘块8中的关键字列表中找到关键字29。
分析上面过程,发现需要3次磁盘I/O操作,和3次内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素。B-Tree相对于AVLTree缩减了节点个数,使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。
2.1.2 B+ Tree
B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。
从上一节中的B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。
B+Tree相对于B-Tree有几点不同:
- 非叶子节点只存储键值信息。
- 所有叶子节点之间都有一个链指针。
- 数据记录都存放在叶子节点中。
将上一节中的B-Tree优化,由于B+Tree的非叶子节点只存储键值信息,假设每个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构如下图所示:
通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。
可能上面例子中只有22条数据记录,看不出B+Tree的优点,下面做一个推算:
InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为〖10〗3)。也就是说一个深度为3的B+Tree索引可以维护103 * 10^3 * 10^3 = 10亿 条记录。
实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在24层。[mysql]()的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要13次磁盘I/O操作。
###2.2、索引的类型
2.2.1 单字段索引
按score列升序排列
2.2.2 复合/组合(多字段)索引
先按userid升序排列再按score降序排列,注意先后顺序
2.2.3 唯一索引
加唯一索引的数据必须唯一不能重复,设置索引后插入重复数据会报错。
2.2.4 地理空间索引(Geospatial Index)
为了支持对地理空间坐标数据的有效查询,MongoDB提供了两种特殊的索引:返回结果时使用平面几何的二维索引和返回结果时使用球面几何的二维球面索引。
2.2.5 文本索引(Text Indexes)
MongoDB提供了一种文本索引类型,支持在集合中搜索字符串内容。这些文本索引不存储特定于语言的停止词(例如“the”、“a”、“or”), 而将集合中的词作为词干,只存储根词。
2.2.6 哈希索引(Hashed Indexes)
为了支持基于散列的分片,MongoDB提供了散列索引类型,它对字段值的散列进行索引。这些索引在其范围内的值分布更加随机,但只支持相等匹配,不支持基于范围的查询。
2.3、索引的使用
2.3.1、查看
db.collection.getIndexes()
例如:
> db.comment.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "articledb.comment"
}
]
结果中显示的是默认 _id 索引,其中
V ----索引引擎版本号
key ----加索引的字段
“_id” : 1 ----升序
Name ----索引名称
ns ----数据库.集合
####2.3.2、创建
语法:
db.collection.createIndex(keys, options)
参数:
Parameter | Type | Description |
keys | document | 包含字段和值对的文档,其中字段是索引键,值描述该字段的索引类型。对于字段上的升序索引,请 指定值1;对于降序索引,请指定值-1。比如: {字段:1或-1} ,其中1 为指定按升序创建索引,如果你 想按降序来创建索引指定为 -1 即可。另外,MongoDB支持几种不同的索引类型,包括文本、地理空 间和哈希索引。 |
options | document | 可选。包含一组控制索引创建的选项的文档。有关详细信息,请参见选项详情列表。 |
options(更多选项)列表:
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. |
提示:
注意在 3.0.0 版本前创建索引方法为 db.collection.ensureIndex() ,之后的版本使用了 db.collection.createIndex() 方法,ensureIndex() 还能用,但只是 createIndex() 的别名。
【示例】
(1)单字段索引示例:对 userid 字段建立索引:
db.collection.createIndex({userid:1})
(2)复合索引:对 userid 和 nickname 同时建立复合(Compound)索引:
db.collection.createIndex({userid:1,nickname:-1})
(3)唯一索引
db.collection.createIndex({userid:1},{unique:"userid"})
2.3.2、 移除
删除 comment 集合中 userid 字段上的升序索引:
db.collection.dropIndex({userid:1})
删除所有索引
db.collection.dropIndexes()
###2.4、索引分析
####2.4.1、索引是否命中
查看根据userid查询数据的情况:
db.comment.find({userid:"1003"}).explain()
(1)无索引情况,“stage” : “COLLSCAN”,表示全集合扫描,注意num为数字不能加引号
db.cstest.find({num:345678}).maxTimeMS(50000).explain()//maxTimeMS最大超时时间
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "cstest.cstest",
"indexFilterSet" : false,
"parsedQuery" : {
"num" : {
"$eq" : 345678
}
},
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"num" : {
"$eq" : 345678
}
},
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"host" : "VM-WIN2008R2",
"port" : 27017,
"version" : "4.0.9",
"gitVersion" : "fc525e2d9b0e4bceff5c2201457e564362909765"
},
"ok" : 1
}
(2)有索引状态"stage" : “IXSCAN”,基于索引的扫描
db.cstest.find({num:125678}).explain('executionStats')
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "cstest.cstest",
"indexFilterSet" : false,
"parsedQuery" : {
"num" : {
"$eq" : 125678
}
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"num" : 1
},
"indexName" : "num_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"num" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"num" : [
"[125678.0, 125678.0]"
]
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,//执行成功的状态
"nReturned" : 1, //返回结果的数量
"executionTimeMillis" : 0,//执行所需时间
"totalKeysExamined" : 1, //索引检查时间
"totalDocsExamined" : 1, //检查文档总数
"executionStages" : {
"stage" : "FETCH", //扫描方式,通过索引抓取数据
"nReturned" : 1, //返回结果的数量
"executionTimeMillisEstimate" : 0,//预估执行时间
"works" : 2, //工作单元数
"advanced" : 1, //优先返回的结果数目
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 1,//扫描的文档个数
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN", //索引方式
"nReturned" : 1,
"executionTimeMillisEstimate" : 0,
"works" : 2,
"advanced" : 1,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"num" : 1
},
"indexName" : "num_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"num" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward", //方向
"indexBounds" : {
"num" : [
"[125678.0, 125678.0]"
]
},
"keysExamined" : 1,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"serverInfo" : {
"host" : "VM-WIN2008R2",
"port" : 27017,
"version" : "4.0.9",
"gitVersion" : "fc525e2d9b0e4bceff5c2201457e564362909765"
},
"ok" : 1
}
2.4.2、 数量占用空间等
可通过compass工具查看