MongoDB 索引维护

本文的运行平台和是windows 7 64bit,Mongodb的版本是3.4,参考了MongoDB索引管理

组合索引的原理

数据库索引最容易理解的是单键索引,它用单独的一个字段建立索引,只要在查询语句中以有效的方式包含了该字段即可命中。

当索引中包含多个字段的时候,这个时候实际建立索引的原则其实和传统数据库例如Mysql类似,遵循最左前缀法则 即以最左边为起点任何连续的索引都能匹配上,且每匹配多一级就可能会减小一部分筛选范围。

例如存在一个索引为 a_1_b_1_c1,当我们查询 a、a+b 和 a+b+c 时都可以命中它,但b、b+c 和 c 则无法命中

获取MongoDB的执行计划

获得粗略的执行计划

Mongo 也为我们提供了 explain 方法用于分析一个语句的执行计划,通过执行计划我们可以了解到关于查询性能方面的很多信息,包括是否命中索引、如何命中索引、执行耗时、文档扫描数等,是性能优化最基本的分析手段

use HTCDR;
db.getCollection("HT_EXAM_REPORT").find({"jzlsh":"zy100000"}).explain();

输出:

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "HTCDR.HT_EXAM_REPORT",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "jzlsh" : {
                "$eq" : "zy100000"
            }
        },
        "winningPlan" : {   //真正执行的查询计划
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",  //用到了索引,我们要避免出现 COLLSCAN字段(全表扫描)
                "keyPattern" : {
                    "jzlsh" : 1,
                    "jgdm" : 1
                },
                "indexName" : "jzlsh_1_jgdm_1",  //查询用到的索引名称
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "jzlsh" : [ ],
                    "jgdm" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "jzlsh" : [
                        "[\"zy100000\", \"zy100000\"]"
                    ],
                    "jgdm" : [
                        "[MinKey, MaxKey]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]  //拒绝的查询计划
    },
    "serverInfo" : {
        "host" : "iZ23gt4a82qZ",
        "port" : 27017,
        "version" : "3.4.10-68-gf1f3809",
        "gitVersion" : "f1f38099c3c964cc445f4805de0ce072b436e5cc"
    },
    "ok" : 1
}

Mongo 会通过优化分析选择其中一种更好的方案放置到 winningPlan,最终的执行计划是 winningPlan 所描述的方式。
其它稍次的方案则会被放置到 rejectedPlans 中

如果只想关注获胜的查询计划,可以通过以下命令直接过滤出获胜的查询计划

db.getCollection("HT_EXAM_REPORT").find({"jzlsh":"zy100000"}).explain().queryPlanner.winningPlan;
输出:
{
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "IXSCAN",
        "keyPattern" : {
            "jzlsh" : 1,
            "jgdm" : 1
        },
        "indexName" : "jzlsh_1_jgdm_1",
        "isMultiKey" : false,
        "multiKeyPaths" : {
            "jzlsh" : [ ],
            "jgdm" : [ ]
        },
        "isUnique" : false,
        "isSparse" : false,
        "isPartial" : false,
        "indexVersion" : 2,
        "direction" : "forward",
        "indexBounds" : {
            "jzlsh" : [
                "[\"zy100000\", \"zy100000\"]"
            ],
            "jgdm" : [
                "[MinKey, MaxKey]"
            ]
        }
    }
}

获取查询的执行阶段

一个查询总执行流程分为若干个 stage(阶段),一个 stage 的分析基础可以是其它 stage 的输出结果,比如扫描索引IXSCAN(索引扫描),然后根据索引记录的地址,去磁盘中获取实际记录( FETCH 的方式提取到各个位置所对应的完整文档);又或者没有用上索引而开始全表扫描(Collection Scan)

  • nReturned:执行返回的文档数
  • executionTimeMillis: 执行时间(ms)
  • totalKeysExamined:索引扫描条数
  • totalDocsExamined:文档扫描条数
  • executionStages:执行步骤
db.getCollection("HT_EXAM_REPORT").find({ "jzlsh": "zy100000" }).explain('executionStats').executionStats


输出:


{
    "executionSuccess" : true,
    "nReturned" : 25,
    "executionTimeMillis" : 0,
    "totalKeysExamined" : 25,
    "totalDocsExamined" : 25,
    "executionStages" : {
        "stage" : "FETCH",
        "nReturned" : 25,
        "executionTimeMillisEstimate" : 0,
        "works" : 26,
        "advanced" : 25,
        "needTime" : 0,
        "needYield" : 0,
        "saveState" : 0,
        "restoreState" : 0,
        "isEOF" : 1,
        "invalidates" : 0,
        "docsExamined" : 25,
        "alreadyHasObj" : 0,
        "inputStage" : {
            "stage" : "IXSCAN",
            "nReturned" : 25,
            "executionTimeMillisEstimate" : 0,
            "works" : 26,
            "advanced" : 25,
            "needTime" : 0,
            "needYield" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "invalidates" : 0,
            "keyPattern" : {
                "jzlsh" : 1,
                "jgdm" : 1
            },
            "indexName" : "jzlsh_1_jgdm_1",
            "isMultiKey" : false,
            "multiKeyPaths" : {
                "jzlsh" : [ ],
                "jgdm" : [ ]
            },
            "isUnique" : false,
            "isSparse" : false,
            "isPartial" : false,
            "indexVersion" : 2,
            "direction" : "forward",
            "indexBounds" : {
                "jzlsh" : [
                    "[\"zy100000\", \"zy100000\"]"
                ],
                "jgdm" : [
                    "[MinKey, MaxKey]"
                ]
            },
            "keysExamined" : 25,
            "seeks" : 1,
            "dupsTested" : 0,
            "dupsDropped" : 0,
            "seenInvalidated" : 0
        }
    }
}




executionStages 这个字段的查看顺序是从内而外的,即实际先执行的是子 stage,再往上逐级执行父 stage,上面的例子就是先IXSCAN
扫描索引jzlsh_1_jgdm_1,然后根据索引上的地址去磁盘去获取完整的记录(FETCH)

Stage的类型

参考MongoDB干货系列2-MongoDB执行计划分析详解(2),Stage有以下类型,其对应意思是

Stage名称Stage含义
COLLSCAN全表扫描
IXSCAN索引扫描
FETCH根据索引去检索指定document
SHARD_MERGE将各个分片返回数据进行merge
SORT表明在内存中进行了排序(与老版本的scanAndOrder:true一致)
LIMIT使用limit限制返回数
SKIP使用skip进行跳过
IDHACK针对_id进行查询
COUNT利用db.coll.explain().count()之类进行count运算
COUNTSCANcount不使用用Index进行count时的stage返回
COUNT_SCANcount使用了Index进行count时的stage返回
SUBPLA未使用到索引的$or查询的stage返回
TEXT使用全文索引进行查询时候的stage返回
PROJECTION限定返回字段时候stage的返回

不希望看到包含如下的stage:

COLLSCAN(全表扫),SORT(使用sort但是无index),不合理的SKIP,SUBPLA(未用到index的$or)

对于count查询,希望看到的有:

COUNT_SCAN

不希望看到的有:

COUNTSCAN

参考 MongoDB干货系列2-MongoDB执行计划分析详解(3)博客中有一个优化查询的例子,讲的非常好

建立索引的语法

详情参考 Mongodb官方手册

db.collection.createIndex(keys, options)


keys:是一个文档 描述在哪些字段上建立索引
options: 可选的,控制索引建立的细节

options 有以下的参数

option参数名称options参数含义
background布尔类型,可选的。 true代表在后台构建索引,不会阻止其他数据库在该集合上的活动,默认值为false。
unique布尔类型,可选的, 创建唯一索引
name字符串类型,可选的, 指定创建的索引名称
partialFilterExpressiondocument类型.如果指定,MongoDB只会给满足过滤表达式的记录建立索引.
sparse布尔类型,可选的, 文档中不存在的字段数据不启用索引。默认值是 false
expireAfterSecondsinteger,指定索引的过期时间
unique布尔类型,可选的, 创建唯一索引

创建索引的语句,常用

给user集合建正序索引:
db.users.createIndex({"name":1})

给name字段创建倒序索引:
db.users.createIndex({"name":-1})

给name,age字段创建组合索引
db.users.createIndex({"name":1,"age":1})

在后台给age字段创建索引(推荐加上生产环境加上background参数)
db.users.createIndex({"age":1},{background:1})

在前台创建索引期间会锁定数据库,会导致其它操作无法进行数据读写,在后台创建索引是,会定期释放写锁,从而保证其它操作的运行,但是后台操作会在耗时更长,尤其是在频繁进行写入的服务器上。

查看索引

getIndexes()方法可以用来查看集合的所有索引
totalIndexSize()查看集合索引的总大小


db.HT_EXAM_REPORT.getIndexes();
[
    {
        "v" : 2,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "HTCDR.HT_EXAM_REPORT"
    },
    {
        "v" : 2,
        "key" : {
            "jzlsh" : 1,
            "jgdm" : 1
        },
        "name" : "jzlsh_1_jgdm_1",
        "ns" : "HTCDR.HT_EXAM_REPORT"
    },
    {
        "v" : 2,
        "key" : {
            "sfzh" : 1,
            "bgsj" : -1,
            "mzzybz" : 1,
            "jgdm" : 1,
            "xgbz" : 1,
            "jgmc" : 1
        },
        "name" : "sfzh_1_bgsj_-1_mzzybz_1_jgdm_1_xgbz_1_jgmc_1",
        "ns" : "HTCDR.HT_EXAM_REPORT"
    }
]


db.HT_EXAM_REPORT.totalIndexSize();
输出:
348160

删除索引

不再需要的索引,我们可以将其删除,mongodb提供两种删除索引的方法:
dropIndex()方法用于删除指定的索引
dropIndexes()方法用于删除全部的索引

删除了user集合上 名称为name_1的索引
db.users.dropIndex("name_1")


删除user表上所有的索引(_id索引除外)
db.users.dropIndexes();

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值