MongoDB权威指南--索引

mongoDB

MongoDB:The Definitive Guide V2 阅读笔记

索引

学习mongodb索引之前,需要先了解下explain函数,这个函数会告诉你mongoDB语句的执行过程。
explain 支持分析find、aggregate、count、group、count、distinct、remove以及update方法
用法如下:

db.collection.explain().find({})

explain 支持三种模式:

  • queryPlanner (默认模式),执行“查询优化器”,给出最优的查询方案。
  • executionStats,执行查询优化器,并选择最优方案执行,并给出语句执行统计结果
  • allPlansExecution,所有的方案都执行一遍,返回执行结果
    注:写操作时,即使是executionStats模式也不会修改数据

explain 输出结果

结果包含三部分:queryPlanner、executionStats(queryPlanner模式没有)、serverInfo
官方文档:Explain Results

queryPlanner
{
    "namespace" : "stock.exchangeDetail", // 表的命名空间,<database>.<collection>
    "indexFilterSet" : false, // 是否使用了Index Filters,
    // 这个相当于一个queryPlanner的缓存,
    // mongo并不会每次都去分析哪个索引方案更优,分析了一次之后,就会把结果缓存,
    // 直到过期或者被清除。详细看[Index Filters](https://docs.mongodb.com/v3.6/core/query-plans/#index-filters)
    "winningPlan" : { // 不一定是最优的
        "stage" : "LIMIT",  // stage名称
        // 常见stage:
        // COLLSCAN 全表扫描
		// IXSCAN 扫描索引
		// FETCH 获取数据,这个就相当于从数据库文件查询数据,当查询条件和返回结果只涉及索引字段的时候,就没有FETCH,也就是说不需要读取数据库文件,官方文档称之为“Covered Queries”
		// SHARD_MERGE  merge分片的数据
		// SHARDING_FILTER for filtering out orphan documents from shards
        "limitAmount" : 2.0, 
        "inputStage" : {
			
		},
		"inputStages": [] // $or 语句会导致出现这个
		"rejectedPlans": [] // 未被选择的plan 
}
executionStats
{
	"executionSuccess" : true,
	"nReturned" : 100, // 匹配到的文档数量
	"executionTimeMillis" : 123, // plan选择和查询 所画时间
	"totalKeysExamined" : 12, // 索引被扫描的次数
	"totalDocsExamined" : 150,// 文档被扫描的次数,一个文档可能被扫描多次
	"executionStages": {
		"stage": "",
		"works": 100, // work unit的数量,work unit 可以是一次索引查询,单个文档读取,project操作
		"advanced": 100, // 中间结果返回数量,通常等于nReturned
		"needTime": 1, // 不会返回中间结果的work cycles数量,比如查询索引
		"needYield": 0, // 存储层请求查询任务暂停的次数,查询中途有数据更新,会先更新然后返回最新结果,这时候就要暂停查询
		"saveState": 10, //	查询任务暂停并存储执行状态的次数
		"restoreState": 10 // 
		"isEOF": 1 // 是否一定没有有符合条件的数据,比如用了limit,那么可能只扫描了一部分就返回了数据,后面可能还有满足条件的文档。
		"inputStage": {
			"keysExamined": 1, // 索引被遍历的次数
			"docsExamined"100, // 被扫描的文档数
			"seeks": 1, // 定位索引的次数,比如$in语句,就需要定位多次索引
		}
	}
}

索引类型

单字段索引
  • 可以是单个字段,也可以是子文档字段
  • 创建索引的顺序,和实际查询语句指定的排序顺序可以不同,不会导致重排序
  • 如果索引字段为文档,则要求文档字段内容和顺序都必须一样,才能匹配
复合索引

即组合多个字段,建立一个多级嵌套的索引,因此字段顺序特别重要。比如索引{ item: 1, stock: 1 },那么,先在item上面建立索引,如果item相同,则在stock上再加索引。

The order of the fields listed in a compound index is important. The index will contain references to documents sorted first by the values of the item field and, within each value of the item field, sorted by values of the stock field.

也就意味着,索引{ item: 1, stock: 1 } 可以替代 索引{item:1},但是不能替代 {stock:1}

In addition to supporting queries that match on all the index fields, compound indexes can support queries that match on the prefix of the index fields. That is, the index supports queries on the item field as well as both item and stock fields:

排序

索引{ a: 1, b: -1 } 支持 按照{a:1,b:-1} 或 {a:-1,b:1} 排序,但是不支持 { a: -1, b: -1 } or {a: 1, b: 1} 排序,,这是为什么呢?暂时不清楚,待详细学习B树原理。

For a query to use a compound index for a sort, the specified sort direction for all keys in the cursor.sort() document must match the index key pattern or match the inverse of the index key pattern. For example, an index key pattern { a: 1, b: -1 } can support a sort on { a: 1, b: -1 } and { a: -1, b: 1 } but not on { a: -1, b: -1 } or {a: 1, b: 1}.

多重索引(Multikey Indexes)

字段为数组时,创建的所以为Multikey索引,相当于一个文档有多个索引指向它。

To index a field that holds an array value, MongoDB creates an index key for each element in the array.

限制
  • 如果是复合索引,只允许一个字段为数组
  • 单个文档索引字段只能有一个是数组,每个文档的数组字段可以不同
查询
  • 当对数组字段进行查询时,多重查询条件 可能会出现非期望的结果,示例如下:
{ _id: 1, item: "ABC", ratings: [ 2, 9 ] }
{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }
db.collection.find({ ratings: { $gte: 3, $lte: 6 } })
// 你想要的结果是 ratings里面存在一个数x,3 <= x <= 6
// 但实际上,查出来的确是 ratings 里面有一个数 >= 3 且 有一个数 <= 6

这时候需要使用$elemMatch命令,限定某个元素满足多个条件

db.collection.find({ ratings: { $elemMatch: { $gte: 3, $lte: 6 } } })
全文索引(Text Indexes)

待研究

2dsphere 索引,地理位置

A 2dsphere index supports queries that calculate geometries on an earth-like sphere

TTL索引

TTL indexes are special single-field indexes that MongoDB can use to automatically remove documents from a collection after a certain amount of time or at a specific clock time. Data expiration is useful for certain types of information like machine generated event data, logs, and session information that only need to persist in a database for a finite amount of time.

  • 只能在date类型的字段上或者date数组字段上面加TTL索引
  • 如果字段是数组,则以时间最早的date为准
  • 根据字段的值(并非文档插入时间,但通常这种数据加索引的字段一般就是插入时间)和expireAfterSeconds删除数据
  • 如果字段不是date,不会删除
  • primary 会启动后台线程,定时检查过期文档(60s)并删除,副本集同步primary的删除结果,不会自己去删除
  • 只支持单字段索引,不支持复合索引,即使加了expireAfterSeconds也没用
  • 索引的过期时间一旦设定好了,就不能更改,如需更改,需要删除再重新创建(有个collMod命令可以改,没看)
  • expireAfterSeconds设置为0,意味着这个字段有值,且到了这个时间就会被删除,没值时会一直存在,这样实现了可控、动态的过期时间设定,也就可以针对不同文档设置不同的过期时间。

自动删除功能非常好用!有个场景,表里面部分数据重要性低,但是量大,为了节省空间以及查询效率,就需要自动删除这部分数据,这部分数据写入时,增加expireTime字段,重要数据不加

Unique Index
  • 不重复的index key
  • 可以是复合索引,也可以是Multikey
  • 字段可以为空,空被认为是null,但是只能有一条
Sparse Index
  • 有值就会被加到索引里面去,没有不加,而非稀疏索引,则把为空的字段看成是null,加入到索引中
  • 唯一稀疏索引,可以有多条为空
  • 索引字段为空的文档查询的时候不会被返回(如果查询用了索引)
Partial Index
  • 符合某种条件的文档才加入索引,其他的不加

使用场景:表数据很多,但是符合某种条件的文档很少,但是这部分文档又需要经常查询,这个时候就可以用“部分索引”,既可以提高这部分数据的查询效率,有可以有效降低索引大小,几乎不会影响其他数据写入效率

无法高效使用索引的命令
  • $where
  • $exist
  • $ne、$nin
  • $not 部分高效,看情况
  • 范围查询,比如$lt, $gt,范围大的时候,效率就会低,范围小,效率就高
  • $or,相当于根据多个查询条件(可以分别使用不同的索引)进行查询,然后去除重复项(可能会耗时很久),如果可以,用$in代替,
查询优化器

当查询语句可以匹配到多个索引时,那么查询优化器会尝试找出最优的索引,怎么找呢,原理其实很简单。

  • 所有可能的plan并行执行
  • 最先返回100条结果的获胜,就是最优plan
  • winner 会被缓存,下次有相同查询时,就直接使用这个winner plan
  • 当collection被改变到一定程度、有索引被创建、1000次查询时,会重新评估,重新选出winner

因为是最先返回100条数据则获胜,返回100条结果快,不代表返回所有结果就快,也就意味着可能并不一定就是最好的,,这样的话,可能每次选择的winner 都不是最好的,从而导致查询很慢,这个时候就需要强制指定索引。

什么时候不用索引

针对从大数据集合中,获取少量数据场景,索引可以极大提高检索效率。
使用索引的时候,需要先在索引数据结构中查询到索引值,然后再查实际的文档。比不用索引多一个步骤。

  • 需要查询的数据占总数据比例很高,比如60%
  • 数据量很小,没必要用索引,全表扫描也很快
$natural

按照存储顺序查询数据,做全表扫描。

db.collection.find({}).hint({$natural:1})

对于,Capped Collection,查询效率会很高

Capped Collection

固定集合 是一种提前创建且固定大小的集合,当集合满了之后,新插入文档会替换掉最老的文档。

特点:

  • 文档不能被移动或删除,也能被更新

这是为了保证数据始终以插入的顺序存储

  • 数据存储在一个固定的磁盘空间,可以更快进行写操作
  • 适用于日志类的数据存储
创建capped collection
db.createCollection('collectionName', { capped: true, size: 10000, max: 100 })
// 大小不超过10000字节且不超过100个文档

一旦创建不可修改,可以把普通集合转化为capped集合,但是不能将capped集合转化为普通集合

GridFS

存储大型文件的方式。
GridFS相比于文件系统的优势:

  • 当已经使用了MongoDB的情况下,用GridFS存储文件会更方便,不用再去用文件系统的相关工具
  • GridFS依赖MongoDB自带的副本集和分片的功能,便于数据备份和拓展
  • 可以避免很多文件系统的问题,比如可以存储大量文件,文件系统单个目录存储大量文件会比较卡
  • mongoDB存储数据以2G作为一个单元,这样文件可以存放在一起

缺点:

  • 相对文件系统,会更慢
  • 修改文件只能先删除再写入,更新不方便。
什么是索引?为什么要使用它

索引是特殊的数据结构 (B树),它以易于遍历的形式存储集合数据集的一小部分。 索引存储特定字段或字段集的值,按字段值排序。 索引条目的排序支持高效的等式匹配和基于范围的查询操作

如何选择索引字段
  1. high-cardinality:该字段在表中所有可能的值很多(distinct结果越多),也就意味着可以快速缩小查询范围,比如该字段的值是唯一的,那么查询的时候,可以直接命中一个文档,而不用再去遍历了。像性别(只有2~3种)、门店id(也不会太多)这些就不适合加索引了
如何实施索引并评估效果
创建索引和删除索引的细节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值