3.MongoDB INDEX/EXPLAIN

377 阅读4分钟

1.索引的作用

  • 线上业务一般存储了海量的数据,且业务读写比例往往是读的比例更高,索引可以加速查询效率。
  • 对增删改有一定的影响,因为需要做索引的维护,维护树的平衡,且还存在索引碎片/索引重建问题,一般公司不会整理索引碎片。
  • 数据量很少的时候全表扫描反而更快速,应该合理的建立索引可避免索引维护等影响写入性能,索引应建在经常搜索和排序的字段上。

2.索引的结构

  • mongodb默认的wiredtiger使用的是b+tree算法。数据结构使用平衡多叉树,相对于b树更稳定更快,这个也是参考了关系型数据库的模型。
  • 索引有自己单独的文件 index开头,末尾是wt(wiredtiger引擎),在成本可以承受下将数据和索引分开存储在不同磁盘上。
  • 使用索引,将索引读取到内存中,尽量保持常驻内存可以避免去磁盘查找增加磁盘io。
  • 多个索引下,mongodb会评估使用那个索引,也就是执行计划竞赛机制。

3.索引的类型和限制

索引类型

  • 单列索引/联合索引 这两个最常用,
  • 多建索引 数组类型的索引
  • 地理索引 坐标 导航软件,大众点评使用此索引。
  • 全文索引 单词集合 针对大量字符串内容的。一般用独立搜索引擎框架。
  • 哈希索引 处理哈希字段
  • 唯一索引 索引内容不可重复
  • TTL索引 过期时间
  • 稀疏索引 仅包含存在的字段
  • 局部索引 按条件索引

mongodb索引限制(一般情况下不会超过)

  • 集合中索引,个数不超过64,长度不超过125字符,符合不超过31个字段。
  • 索引存储在内存中,确保索引不超过内存大小的限制。
  • 注意当前版本是否支持你想要创建的索引类型。

4.查询计划竞赛(选择查询计划的机制)

stateDiagram-v2
执行查询语句 --> 没有执行计划
执行查询语句 --> 已有缓存的最优执行计划
没有执行计划 --> 优化器评估
已有缓存的最优执行计划  --> 后期多次调用同一个查询缓存计划

优化器评估 --> 生成多个执行计划
生成多个执行计划 --> 并行运行执行计划保留执行最快的计划
并行运行执行计划保留执行最快的计划 --> 后期多次调用同一个查询缓存计划
后期多次调用同一个查询缓存计划 --> 返回结果
后期多次调用同一个查询缓存计划 --> 修改数据或索引
修改数据或索引 --> 优化器评估

  • 执行查询语句,没有执行计划优化器会评估,如果有缓存过执行计划,则执行计划返回结果。
  • 优化器评估后生成多个执行,并行执行并缓存最优的执行计划。
  • 后期同一个查询多次调用已经缓存的执行计划。
  • 虽然有执行计划,但是修改了数据或索引,则优化器重新评估执行计划并缓存。
  • 查询计划可以更新/删除,PlanCache.cleaer()
  • db.collection.explain()/cursor.explain 返回查询计划和统计信息。

5.创建/删除索引

假设有name和age字段

db.collection.createIndex({name:1})         创建单列索引
db.collection.createIndex({name:1,age:1})   创建复合索引 
db.collection.dropIndex({name:1})           删除集合中指定索引
db.collection.dropIndexes()                 删除集合中所有索引,不删除默认_id索引
db.collection.getIndexes()                  获取集合中所有的索引
db.collection.totalIndexSize()              返回集合使用的总大小

不建议使用的
db.collection.reIndex()                     重建集合中的索引
db.ins.ensureIndex({age:1,name:1})          创建索引,可用因为是createIndex别名。
  • db.collection.createIndex({name:1},{backgroud:true}) 4.2版本之前后台创建索引不阻塞主进程。4.2之后弃用,索引只在开始和结束才占用排它锁其余过程进行交叉的读写操作
  • 删除了索引,查询机会重新评估,必须等待排他锁释放之后才会删除索引。
  • 索引名字是否唯一,是否过期,是不是稀疏索引都可以不考虑。

6.explain/hint 查询计划/强制走特定索引

explain,有三种选择

  • queryPlanner(默认,可以带false表示默认):使用queryPlanner只列出所有可能执行的方案,不会执行实际的语句,显示已经胜出的方案winningPlan。
  • executionStats:使用executionStats只执行winningPlan方案,并输出结果。
  • allPlansExecution(true):使用allPlansExecution执行所有的方案,并输出结果。

hint

  • 可以使用hint来强制指定索引执行。db.collection.find({条件}).hint({你想使用的索引名称})

执行计划的解读

db.ins.find({money:39}).explain()
                //执行计划竞赛胜利的执行计划。
                "winningPlan" : {
                        "stage" : "FETCH",   //匹配
                        "inputStage" : {
                                "stage" : "IXSCAN",  //索引扫描  COLLSCAN为全表扫描
                                "keyPattern" : {
                                        "money" : 1
                                },
                                "indexName" : "money_1",  //使用的索引名称
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "money" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "money" : [
                                                "[39.0, 39.0]" //最大值和最小值
                                        ]
                                }
                        }
                },
                "rejectedPlans" : [       //被抛弃的索引执行计划
                        {
                                "stage" : "FETCH",  //匹配
                                "inputStage" : {
                                        "stage" : "IXSCAN",  //索引扫描
                                        "keyPattern" : {     //索引字段
                                                "money" : 1,
                                                "status" : 1
                                        },
                                        "indexName" : "money_1_status_1",  //索引名称
                                        "isMultiKey" : false,
                                        "multiKeyPaths" : {
                                                "money" : [ ],
                                                "status" : [ ]
                                        },
                                        "isUnique" : false,
                                        "isSparse" : false,
                                        "isPartial" : false,
                                        "indexVersion" : 2,
                                        "direction" : "forward",
                                        "indexBounds" : {
                                                "money" : [
                                                        "[39.0, 39.0]"
                                                ],
                                                "status" : [
                                                        "[MinKey, MaxKey]"
                                                ]
......
......