200418-MongoDb系列教程十三:索引

文章目录
  1. 1. 创建索引
  2. 2. 索引查询
  3. 3. 索引分析
  4. 4. 删除索引
  5. 5. 文档自动删除
  6. 6. 覆盖索引
  • II. 其他
    1. 1. 一灰灰Blog: https://liuyueyi.github.io/hexblog
    2. 2. 声明
    3. 3. 扫描关注
  • 索引一般用来提高查询效率,避免全集合搜索,那么在mongodb中,支持索引么?如果支持,如何定义索引,如何使用索引,如何确定一个sql是否走索引?

    1. 创建索引

    语法定义:

    1
    db.collection.createIndex(keys, options)

    请注意,在3.0之前的版本中,也可以使用ensureIndex来创建索引

    参数说明:

    • keys:kv结构,key为fieldName, value为1 表示升序创建索引;-1 表示降序创建索引;支持多字段索引
    • options:可选参数

    常见参数说明如下表:

    参数名 说明
    background true,则后台方式创建索引,不阻塞其他操作;默认为false
    unique true,则表示唯一约束索引,比如_id就有唯一约束;默认为false
    name 索引名,不指定时,根据field + 方向生成索引名
    sparse true, 则不包含这个字段的不创建索引,且索引查询时查不到不包含这个字段的文档;默认false
    expireAfterSeconds 设置文档在集合的生存时间,s为单位
    v 版本号
    weight 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重
    default_language 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
    language_override 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language

    实例如下:

    1
    db.doc_demo.createIndex({'name': 1}, {'background': true})

    2. 索引查询

    查看一个集合定义了哪些索引,借助getIndexes()方法即可,如

    1
    db.doc_demo.getIndexes()

    3. 索引分析

    虽然我们创建了索引,但是我们的查询语句却并不一定会走索引,在mysql中我们知道有一个explain语句来分析索引情况,在mongodb中也存在类似的方法

    集合数据如下

    1
    2
    3
    4
    5
    6
    7
    8
    { "_id" : ObjectId("5e7b5ac10172dc950171c488"), "name" : "一灰灰blog", "age" : 19, "skill" : [ "java", "python", "sql" ], "tag" : 2 }
    { "_id" : ObjectId("5e7b5ac40172dc950171c489"), "name" : "一灰灰blog", "age" : 20, "skill" : [ "web", "shell", "js" ], "tag" : 1 }
    { "_id" : ObjectId("5e7b5bb085a742842d2e23fc"), "name" : "一灰灰", "age" : 18, "sex" : "man", "tag" : 2 }
    { "_id" : ObjectId("5e7b5c2e0172dc950171c48a"), "name" : "一灰灰", "age" : 18, "hobby" : [ "play game" ] }
    { "_id" : ObjectId("5e7c5627f020f58f5323e52d"), "name" : "一灰灰2", "age" : 22, "skill" : [ "android", "ios" ] }
    { "_id" : ObjectId("5e7c5a61f020f58f5323e52e"), "name" : "一灰灰", "doc" : { "title" : "简单的标题", "content" : "简单的内容", "tag" : [ "java", "后端" ] } }
    { "_id" : ObjectId("5e7c5a8af020f58f5323e52f"), "name" : "一灰灰", "doc" : { "title" : "哈哈", "content" : "嘻嘻哈哈", "tag" : [ "随笔" ], "draft" : true } }
    { "_id" : ObjectId("5e7c5ae7f020f58f5323e530"), "name" : "一灰灰", "doc" : { "title" : "22", "content" : "3333", "tag" : [ "随笔" ], "draft" : false, "visit" : 10 } }

    当前集合上除了默认的_id索引之外,针对name也创建了升序索引

    如需要判断一个查询语句的情况,可以在后面加上explain()方法,如下

    1
    db.doc_demo.find({'name': '一灰灰'}).explain()

    输出如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    {
    "queryPlanner" : {
    "plannerVersion" : 1,
    "namespace" : "basic.doc_demo",
    "indexFilterSet" : false,
    "parsedQuery" : {
    "name" : {
    "$eq" : "一灰灰"
    }
    },
    "winningPlan" : {
    "stage" : "FETCH",
    "inputStage" : {
    "stage" : "IXSCAN",
    "keyPattern" : {
    "name" : 1
    },
    "indexName" : "name_1",
    "isMultiKey" : false,
    "multiKeyPaths" : {
    "name" : [ ]
    },
    "isUnique" : false,
    "isSparse" : false,
    "isPartial" : false,
    "indexVersion" : 2,
    "direction" : "forward",
    "indexBounds" : {
    "name" : [
    "[\"一灰灰\", \"一灰灰\"]"
    ]
    }
    }
    },
    "rejectedPlans" : [ ]
    },
    "serverInfo" : {
    "host" : "0f51c424211c",
    "port" : 27017,
    "version" : "4.0.4",
    "gitVersion" : "f288a3bdf201007f3693c58e140056adf8b04839"
    },
    "ok" : 1
    }

    关于是否走索引,主要看stage,通常会有以下几种状态

    stage 描述
    COLLSCAN 全表扫描
    IXSCAN 扫描索引
    FETCH 根据索引去检索指定document
    SHARD_MERGE 将各个分片返回数据进行merge
    SORT 表明在内存中进行了排序
    LIMIT 使用limit限制返回数
    SKIP 使用skip进行跳过
    IDHACK 针对_id进行查询
    SHARDING_FILTER 通过mongos对分片数据进行查询
    COUNT 利用db.coll.explain().count()之类进行count运算
    COUNTSCAN count不使用Index进行count时的stage返回
    COUNT_SCAN count使用了Index进行count时的stage返回
    SUBPLA 未使用到索引的$or查询的stage返回
    TEXT 使用全文索引进行查询时候的stage返回
    PROJECTION 限定返回字段时候stage的返回

    上面的具体查询,对应的stage组合是Fetch+ixscan,也就是说会根据索引查询

    虽然mongodb会根据查询来选择索引,但并不能保证都能选到最优的索引;这种时候我们可以通过hint来强制指定索引,举例如下

    1
    db.doc_demo.find({'age': 18, 'name':'一灰灰'}).hint({'name': 1}).explain()

    4. 删除索引

    一般有下面两种删除方式,全量删除和指定索引删除

    1
    2
    3
    4
    # 全量删除
    db.collection.dropIndexes()
    # 指定删除
    db.collection.dropIndex(索引名)

    请注意,指定索引名删除时,如果不确定索引名是啥,可以通过getIndexes()来查看

    5. 文档自动删除

    在创建索引的时候,其中有一个参数比较有意思,有必要单独拿出来说明一下,expireAfterSeconds 设置文档的生存时间

    使用它有几个潜规则:

    • 索引字段为Date类型
    • 单字段索引,不支持混合索引
    • 非立即执行
    1
    2
    3
    4
    5
    # 插入一条文档,请注意这个时间,因为时区原因相对于北京时间,少8小时
    db.doc_demo.insert({'name': 'yihui', 'log': '操作了啥啥啥', 'createDate': new Date('Mar27, 2020 2:54:00')})

    # 创建索引
    db.doc_demo.createIndex({'createDate': 1}, {expireAfterSeconds: 60})

    然后过一段时间(并不一定10:55分的时候会删除)再去查询,会发现插入的文档被删除了

    利用这种特性,在mongodb中存一些需要定时删除的数据,相比较我们常用的mysql而言,还是有很大优势的

    6. 覆盖索引

    覆盖索引的概念有些类似mysql中的不回表查询的case,直接查询索引,就可以返回所需要的字段了

    比如在前面的case中,我只查询name字段,可以走覆盖索引;但是返回除了name,还有_id,那么就不能了

    1
    2
    3
    4
    # 覆盖索引
    db.doc_demo.find({'name': '一灰灰'}, {'name': 1, '_id': 0})
    # 非覆盖索引
    db.doc_demo.find({'name': '一灰灰'}, {'name': 1})

    注意:所有索引字段是一个数组时,不能使用覆盖索引

    II. 其他

    1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

    一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

    2. 声明

    尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

    3. 扫描关注

    一灰灰blog

    QrCode

    评论

    Your browser is out-of-date!

    Update your browser to view this website correctly. Update my browser now

    ×