接上期MONGODB 中的数据的快速查找是通过索引来进行的,这里来先把一些INDEX 中MONGODB 在索引中的词汇来捋一捋, 如voverd query 覆盖查询, IXCSAN索引扫描 COLLSCAN 全collection 扫描, Query shape , index prefix 前缀索引 , selectivity 过滤性。
关于INDEX 的种类,MONGODB 主要分为以下种类的INDEX
单建索引
组合索引
多值索引 多键索引
地理位置索引
全文索引
TTL 索引
部分索引
HASH 索引
跳跃索引 稀疏索引
在一个MONGODB 的语句执行中,也有类似与传统数据库的执行方式,首先语句与执行计划中的缓存匹配,如果发现执行过,则可能直接走原有的计划,如不可以,会生成新的候选计划,在评估候选计划,通过对不同的优化方式进行测试,最终得出最优的计划, 创建计划缓存。最终通过执行器将计划进行执行,与其他的数据库类型,可以强制MONGODB 使用预定的索引。
1 ESR 原则
对于MONGODB 重要的第一个索引的关联项和知识点是MONGODB 的组合查询中的配对的索引的 ESR 原则, Equal , sort , range , 对应不同的查询可能会更简化为 ES , ER。
这里的意思简单用一句话来描述, 查询中的索引与查询需要匹配一个 等值计算,排序, 范围 这样一个顺序。
下面通过一个查询和三个索引来说明问题
在一个collection 建立三个索引,其中不同的是索引中字段的顺序
db.zips.createIndex(
{"city": 1,"_id":1,"pop":1},
{background:true} )
db.zips.createIndex(
{"_id":1,"city": 1,"pop":1},
{background:true} )
db.zips.createIndex(
{"pop":1,"_id":1,"city": 1},
{background:true} )
db.zips.find({city:"AGAWAM",pop:{$gt:15337}}).sort("_id": 1).explain("executionStats")
查询中可以看到三个索引,在查询中,使用了根据上面的ESR原则建立的索引,而并没有用其他的索引。
db.zips.dropIndex("city_1__id_1_pop_1")
在我们删除最优选后,在此查询 可以看到查询的方式变为了 SR 模式
db.zips.createIndex(
{"city": 1,"_id":1,"pop":1},
{background:true} )
db.zips.createIndex(
{"_id":1,"city": 1,"pop":1},
{background:true} )
db.zips.createIndex(
{"pop":1,"_id":1,"city": 1},
{background:true} )
db.zips.find({city:"AGAWAM",pop:{$gt:15337}}).sort("_id": 1).explain("executionStats")
db.zips.dropIndex("city_1__id_1_pop_1")
db.zips.dropIndex("pop_1__id_1_city_1")
依次对索引进行删除可以看到如何ESR 原理的查询和索引性能是最优的,其次是SR。 每种不同的索引会导致 docsExamined 评估数的不同
这个例子的优化点用一句话表达就是,缩小范围,顺序定位,缩小范围,减少最后的排序组合,这样在多个字段的索引中,定位是最快的,避免搜集过多的document ,在进行filiter.
2 全文索引
MONGODB 在2.4就已经有了全文索引,全文索引的没有在MONGODB 中大量使用的原因是数据量大了后,性能的问题。 在MONGODB 4.2后的Altas的全文索引采用了新的方法,有兴趣的可以查询相关的内容。这里还是说老的mongodb community 的中的FULL TEXT . MONGODB 的全文索引,1个collection中只能拥有一个全文索引。
全文索引需要注意的是一个查询中只能有一个全文索引,并且不能在全文索引中使用HINT,同时也不能使用$natural 进行排序,同时如果有地理数据查询时不能预地理索引一起进行查询。对于中午的全文索引查询也要注意一些特殊的情况和设置。
3 跳跃索引 或 稀疏索引
Mongodb 属于NOSQL 数据库库,其中最主要的特点是每个document (ROWS) 都可能是不一样的,而索引本身的特性就是对于操作的数据要求每行都存在,这里就产生了矛盾。
稀疏索引就是解决这个问题的,
db.places.createIndex({"name": 1}, {sparse: true ,background:true})
通过在 create index 时添加 sparse 稀疏索引降低索引在无KEY VALUE 的情况下不会对这个document 的不存在的KEY 进行 NULL 的索引的建立。
4 部分索引
部分索引并不是类似MYSQL 的前缀索引,而是根据条件来建立索引,可以这样理解一部分数据在collection中建立索引,一部分数据在collection不建立索引。下面是一个例子,我们仅仅对pop 中数据库大于1000的数据进行索引的操作。那么这个索引的好处也很明显,在符合你的条件的基础上的查询都可以走索引,索引的容量小,效率高。
db.zips_purea.createIndex({pop:1},{partialFilterExpression:{pop:{$gt:1000}}})
db.zips_purea.find({pop:{$gt:1200}}).explain()
条件变化了,已经不再我们的条件之内了,可以看见查询就不再使用索引了,为什么,因为索引没有建立,一部分数据并没有
5 TTL 索引
MONGODB 的TTL 索引或者叫 expired time INDEX , TTL 索引主要的公用就是数据的过期清理,REDIS 本身是可以对键值进行设置,通过键值的过期时间来在规定的时间将键值清理的,MONGODB 为什么也会有这样的一个功能,个人从这几年的MONGODB 的使用的过程中,深深的体会这个功能的重要性,MONGODB 本身承载的数据,不同应用的报文承接,日志记录的功能,用户信息的查询等等,这些场景的应用中都有一个不可回避的问题,就是数据的时效性。数据在这些场景中都是有时效性的,过期后数据就不在被使用,一般的数据库都不会具有自动清理数据的功能,MOGNODB 通过TTL 索引设置时间的方式,将数据到期后,根据数据库本身性能的要求,在满足数据库空闲时进行过期数据的清理的工作。
我们打开一个COLLECTION,并且其中有一条记录是包含时间的,这里TTL索引加你了以data2 为字段,超过300秒就开始清理数据的TTL 索引。
db.data.createIndex({"data2":1},{expireAfterSeconds:300})
在索引建立后,300秒后,我们查看到底数据有没有被清理出去, 再次查询,的确数据已经被删除了。
但,但就怕说但,很多同学反馈说,建立了索引不能删除数据那么可以看看是否有以下的问题
1 字段的类型,字段的类型必须是ISODATE 类型,文本格式的数据是无法被识别的。
2 时间在数据组中,如果时间在数组中包含多个时间值,以时间最早的作为清理的基准(但是在不建议这样做,还是规规矩矩的建立一个时间KEY VALUE 很难吗)
3 文档中并不包含这个KEY VALUE ,则这个文档永远不会被删除
4 数据库处于繁忙,未达到可以清理的阀值的情况下,数据库会进行轮训,达到轮训时间,但系统性能不达标的情况下,不会清理数据,系统默认60秒轮训一次。
5 从库的数据操作,并不通过TTL索引,相关操作是通过主库OP LOG 推送的。
6 TTL 索引本身具有普通INDEX的功能,一箭双雕。
6 HASH 索引
HASH 索引本身的功能是在MONGODB 分片的情况下进行工作的,也就是并不是普通的复制集,或单体的MONGODB 有这个需求,或功能。 建立这个功能也是通过针对 _id 主键加索引的方式。
在MONGODB 4.4 hash index 本身已经支持了compound hash index 这里由于本身的特性以及使用场景,就不在深入了。这里多说一句关于MOGNODB数据分片的问题,MONGODB 分片 HASH RANGE 这两种方式都会针对不同的业务场景,也都有各自的缺陷,用好了性能提升,用不好,性能和单机相比会有很大的衰减,所以不要一提分片就认为是解决问题的第一个方法。
7 地理索引 2d indexes
地理索引面向的是MONGODB 的地理数据查询方式,通过地理索引可以快速的处理地理数据信息。
具体的使用方式,需要地理查询的特殊方式,这里不再详述,需要了解详情的的同学可以打开下方的连接。
Mongodb GeoJSON 地理数据处理 其实我也很厉害
https://mp.weixin.qq.com/s?__biz=Mzg4NDA0NTEwNA==&mid=2247494621&idx=1&sn=5ee53df3b1cae7835d529e2efda61347&chksm=cfbc8f82f8cb069490a31fe57cca6d4397dab23686ee1b755ddc5fb08d770ce5e29333765889&token=959999716&lang=zh_CN#rd
小结:
mongodb 本身的索引种类与传统数据库部分相同,部分不同,不同的部分以自身的MONGODB数据的类型,数据的存储方式,以及业务场景有关。所以用好MONGODB 的第一个条件,就是识别业务场景,那些业务场景适合,剩下的才是MONGODB 的数据存储设计(schema )与 索引的性能支持。