MongoDB 索引原理与索引优化
转载请注明出处:
1.MongoDB索引
索引通常能够极大的提高查询的效率, 如果没有索引, MongoDB 在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的, 特别在处理大量的数据时, 查询可以要花费几十秒甚至几分钟, 这对网站的性能是非常致命的。
2.MongoDB索引说明
索引数据通过 B 树来存储,从而使得搜索的时间复杂度为 O(logdN) 级别的(d 是 B 树的度, 通常 d 的值比较大,比如大于 100),比原先 O(N) 的复杂度大幅下降。这个差距是惊人的,以一个实际例子来看,假设 d=100,N=1亿,那么 O(logdN) = 8, 而 O(N) 是 1亿。是的,这就是算法的威力。
索引本身是在高速缓存当中,相比磁盘 IO 操作会有大幅的性能提升。(需要注意的是,有的时候数据量非常大的时候,索引数据也会非常大,当大到超出内存容量的时候,会导致部分索引数据存储在磁盘上,这会导致磁盘 IO 的开销大幅增加,从而影响性能,所以务必要保证有足够的内存能容下所有的索引数据)
当然,事物总有其两面性,在提升查询速度的同时,由于要建立索引,所以写入操作时就需要额外的添加索引的操作,这必然会影响写入的性能,所以当有大量写操作而读操作比较少的时候,且对读操作性能不需要考虑的时候,就不适合建立索引。当然,目前大多数互联网应用都是读操作远大于写操作,因此建立索引很多时候是非常划算和必要的操作。
-
B树在查询中的比较是在内存中完成的,相比磁盘IO的速度,内存中的比较耗时几乎可以忽略。所以只要树的高度足够低,IO次数足够少,就可以提升查找性能。
-
B树为了插入一个元素,多个节点发生了连锁改变,会有一定的性能损耗,但也正因为如此,B树能够始终维持多路平衡。这也是B树的另外一大优势:自平衡。
-
查找的元素在不同的结点(根结点、中间结点、叶子结点),性能会有一定差别,因此查询性能不稳定。
-
范围查找性能不高。
-
我们知道二叉查找树查询的时间复杂度是O(logN),查找速度最快和比较次数最少,既然性能已经如此优秀,但为什么实现索引是使用B-Tree而不是二叉查找树,关键因素是磁盘IO的次数。
磁盘读取依靠的是机械运动,分为寻道时间、旋转延迟、传输时间三个部分,这三个部分耗时相加就是一次磁盘IO的时间,大概9ms左右。这个成本是访问内存的十万倍左右。正是由于磁盘IO是非常昂贵的操作,所以数据库性能优化的核心思想是降低磁盘IO次数。
说明: 普通的机械盘HDD一次磁盘IO的时间大概是9ms; 普通SSD一次磁盘IO耗时大概是0.2ms(IOPS:5000); PCIe卡一次磁盘IO耗时大概是0.05ms(IOPS:20000);
从二叉树的查找过程了来看,最坏的情况下磁盘IO的次数由树的高度来决定。要减少磁盘IO的次数就必须要压缩树的高度,让瘦高的树尽量变成矮胖的树,所以B-Tree就在这样伟大的时代背景下诞生了。
db_name.table_Name.find({query}).explain(cond)
参数
名称 | 描述 |
---|---|
db_name | 数据库名 |
table_Name | 集合名 |
query | 查询条件 |
cond | 查询计划所使用的参数 |
返回值
参数 | 含义 |
---|---|
plannerVersion | 查询计划版本 |
namespace | 要查询的集合 |
indexFilterSet | 是否使用索引 |
parsedQuery | 查询条件,此处为x=1 |
winningPlan | 最佳执行计划 |
stage | 查询方式,见下表 |
filter | 过滤条件 |
direction | 搜索方向 |
rejectedPlans | 拒绝的执行计划 |
serverInfo | MongoDB服务器信息 |
stage说明
参数 | 含义 |
---|---|
COLLSCAN | 全表扫描 |
IXSCAN | 索引扫描 |
FETCH | 根据索引去检索文档 |
SHARD_MERGE | 合并分片结果 |
IDHACK | 针对 _id 进行查询 |
2.executionStats:executionStats会返回执行计划的一些统计信息
参数 | 含义 |
---|---|
executionSuccess | 是否执行成功 |
nRetured | 返回的文档数 |
executionTimeMillis | 执行耗时 |
totalKeysExamined | 索引扫描次数 |
totalDocsExamined | 文档扫描次数 |
stage | 扫描方式,具体可选值与上下的相同 |
nRetured | 查询document获得数据的时间 |
executionTimeMillsEstimate | 检索document获得数据的时间 |
inputStage.executionTimeMillisEstimate | 该查询扫描文档index所用时间 |
works | 工作单元数,一个查询会分解成小的工作单元 |
advanced | 优先返回的结果数 |
docsExamined | 文档检查数目,与totalDocsExamined一致,检查了总共的document个数,从而返回上面的nReturned数量 |
在实际分析索引问题是否最优的时候,主要查看executionStats.totalKeysExamined、
executionStats.totalDocsExamined、executionStats .nReturned三个统计项,如果存在以下情况则说明索引存在问题,可能索引不是最优的:
-
-
executionStats. totalDocsExamined远大于executionStats .nReturned