MongoDB的索引

1、简介

它就像是一本书的目录,如果没有它,我们就需要对整个书籍进行查找来获取需要的结果,即所说的全盘扫描;

而有了目录(索引)之后就可以通过它帮我们定位到目标所在的位置,快速的获取我们想要的结果。

2、演示

第一步,向用户集合users中插入100W条数据

var insertUsers = function() {
  var start = new Date().getTime();
  for (var i = 1; i <= 1000000; i++) {
    db.users.insert({
      "userid": i,
      "username": "wjg" + i,
      "age": Math.floor(Math.random() * 100), //年龄为0~99的随机整数
      "createdate": new Date()
    })
  }
  var end = new Date().getTime();
  print("插入100W条数据共耗时" + (end - start) / 1000 + "秒");
}

LZ的渣渣I3和4G内存总共耗时了484.623秒,约8分多钟。任务管理器里边可以很清楚的看到当时CPU、内存和磁盘使用率都普遍的增高。

第二步:查询用户名为“wjg465413”的文档对象

db.users.find({username:"wjg465413"}).explain("allPlansExecution")
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "username" : {
                "$eq" : "wjg465413"
            }
        },
        "winningPlan" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "username" : {
                    "$eq" : "wjg465413"
                }
            },
            "direction" : "forward"
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1,
        "executionTimeMillis" : 865,
        "totalKeysExamined" : 0,
        "totalDocsExamined" : 1000000,
        "executionStages" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "username" : {
                    "$eq" : "wjg465413"
                }
            },
            "nReturned" : 1,
            "executionTimeMillisEstimate" : 770,
            "works" : 1000002,
            "advanced" : 1,
            "needTime" : 1000000,
            "needFetch" : 0,
            "saveState" : 7813,
            "restoreState" : 7813,
            "isEOF" : 1,
            "invalidates" : 0,
            "direction" : "forward",
            "docsExamined" : 1000000
        },
        "allPlansExecution" : [ ]
    },
    "serverInfo" : {
        "host" : "Jack",
        "port" : 27017,
        "version" : "3.0.3",
        "gitVersion" : "b40106b36eecd1b4407eb1ad1af6bc60593c6105"
    },
    "ok" : 1
}

说明:这里的explain方法相当于查询计划,它会返回给你查询过程的详细信息。它的参数有三种模式:“queryPlanner”(查询计划[默认])、“executionStats”(执行状态)和“allPlansExecution”(所有执行计划),这里我们只关注它返回给我们的以下几个信息。

"executionTimeMillis" : 865 //执行的毫秒数 注:如果你是第一次执行,可能会花费更长的时间
"totalDocsExamined" : 1000000  //共检查的文档数

第三步:在用户名“username”字段上加上索引

db.users.createIndex({ "username" : 1 }) 

重新执行上次的查询操作

db.users.find({username:"wjg465413"}).explain("allPlansExecution")
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "username" : {
                "$eq" : "wjg465413"
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "username" : 1
                },
                "indexName" : "username_1",
                "isMultiKey" : false,
                "direction" : "forward",
                "indexBounds" : {
                    "username" : [
                        "[\"wjg465413\", \"wjg465413\"]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1,
        "executionTimeMillis" : 53,
        "totalKeysExamined" : 1,
        "totalDocsExamined" : 1,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 1,
            "executionTimeMillisEstimate" : 0,
            "works" : 2,
            "advanced" : 1,
            "needTime" : 0,
            "needFetch" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "invalidates" : 0,
            "docsExamined" : 1,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 1,
                "executionTimeMillisEstimate" : 0,
                "works" : 2,
                "advanced" : 1,
                "needTime" : 0,
                "needFetch" : 0,
                "saveState" : 0,
                "restoreState" : 0,
                "isEOF" : 1,
                "invalidates" : 0,
                "keyPattern" : {
                    "username" : 1
                },
                "indexName" : "username_1",
                "isMultiKey" : false,
                "direction" : "forward",
                "indexBounds" : {
                    "username" : [
                        "[\"wjg465413\", \"wjg465413\"]"
                    ]
                },
                "keysExamined" : 1,
                "dupsTested" : 0,
                "dupsDropped" : 0,
                "seenInvalidated" : 0,
                "matchTested" : 0
            }
        },
        "allPlansExecution" : [ ]
    },
    "serverInfo" : {
        "host" : "Jack",
        "port" : 27017,
        "version" : "3.0.3",
        "gitVersion" : "b40106b36eecd1b4407eb1ad1af6bc60593c6105"
    },
    "ok" : 1
}

可以看到两次的查询计划有很大的差别,我们还是着重看下那两个属性值。

 "executionTimeMillis" : 53  //执行的毫秒数

 "totalDocsExamined" : 1  //共检查的文档数

加过索引之后查询这个文档所耗费的时间仅仅为53毫秒,并且扫描一次直接定位,性能提升了16倍。可见合理使用索引的重要性!

注:“_id”字段是Mongo为我们默认添加的索引,而且是唯一索引,保证了数据的唯一性,不可以移除。另外,使用limit(1)限制查询结果的数量也可以提高查询速度

3、索引的类型

a)、单一索引:可以在数据集上任意一个字段上建立索引,包括普通的属性键、内嵌文档以及内嵌文档中的属性键。

db.users.createIndex({ "username" : 1 })  //普通属性键的索引

//假设class是一个内嵌的文档
db.users.createIndex({ "class" : 1 })  //内嵌文档的索引

db.users.createIndex({ "class.classname" : 1 })  //内嵌文档中的属性键索引

索引方向:1表示升序,-1表示降序

b)、复合索引:以多个属性键为基础而建立得索引

db.users.createIndex({ "username" : 1, "age" : -1, "userid" : 1 })  //在“username”、“age”和“userid”上建立复合索引

索引前缀:通过建立上边的复合索引之后,Mongo就相当于同时拥有了三个索引一样,分别是{"username" : 1},{"username" : 1, "age" : -1}和{"username" : 1, "age" : -1, "userid" : 1},但是像{"age" : -1},{"userid" : 1}或者{"age" : -1, "userid" : 1}这三个索引并不会起作用。所以它会使用包含了前缀(首个)的索引的作为复合索引

c)、多键索引:为数组中的多个值建立索引以实现高效查询。

注:Ⅰ、不允许在多个数组上建立复合索引

  Ⅱ、不能指定片键作为多键索引

  Ⅲ、哈希索引不能是多键

  Ⅳ、多键索引不支持覆盖查询

d)、地理空间索引和查询:Mongo提供了两种曲面类型的索引:2dsphere索引和2d索引。查询类型包括:包含(inclusion),交叉(intersection)和接近(proximity)

e)、文本索引:用来支持查询包含了字符串或者字符串数组的文档

db.users.createIndex({"username" : "text"})

注:文本索引不支持排序并且一个复合文本索引不能再包含其他任何索引了

f)、哈希索引:它可以在使用了哈希片键进行分片的数据集上进行索引,支持相等查询,但是不支持范围查询

 db.users.createIndex({"username" : "hashed"})

4、索引特性

a)、TTL(Time-To-Live)索引:是一种具有生命周期的索引,它允许为每一个文档设置一个超时时间

 db.users.createIndex({ "createdate" : 1 },{ "expireAfterSecs" : 60*60*24 }) 

说明:在“createdate”字段上建立一个TTL索引,当这个自段存在并且是日期类型,当服务器时间比“createdate”字段的时间晚60*60*24秒,即24小时时,文档就会被删除

b)、唯一索引:确保集合的每一个文档的指定键都有唯一值

db.users.createIndex({"username" : 1}, {"unique" : true})

c)、稀疏索引:Mongo里边的null会被看做值,如果有一个可能存在也可能不存在的字段,我们可以使用稀疏索引

db.users.createIndex({"age" : 1},{"sparse" : true})

4、索引操作

a)、查看所有索引

db.users.getIndexes()

b)、移除索引

db.users.dropIndex({"createdate1" : 1 })

c)、移除所有索引

db.users.dropIndexes()

d)、重建索引

db.users.reIndex()

说明:该操作会先删除所有索引,包括“_id”,然后重新创建所有索引

(0)

相关推荐

  • MongoDB TTL索引的实例详解

    MongoDB TTL索引的实例详解 TTL索引是一种特殊类型的单字段索引,主要用于当满足某个特定时间之后自动删除相应的文档.也就是说集合中的文档有一定的有效期,超过有效期的文档就会失效,会被移除.也即是数据会过期.过期的数据无需保留,这种情形适用于如机器生成的事件数据,日志和会话信息等等.本文主要描述TTL索引的使用. 一.TTL索引 创建方法 db.collection.createIndex(keys, options) options: expireAfterSeconds 指定多少秒或

  • 基于MongoDB数据库索引构建情况全面分析

    前面的话 本文将详细介绍MongoDB数据库索引构建情况分析 概述 创建索引可以加快索引相关的查询,但是会增加磁盘空间的消耗,降低写入性能.这时,就需要评判当前索引的构建情况是否合理.有4种方法可以使用 1.mongostat工具 2.profile集合介绍 3.日志 4.explain分析 mongostat mongostat是mongodb自带的状态检测工具,在命令行下使用.它会间隔固定时间获取mongodb的当前运行状态,并输出.如果发现数据库突然变慢或者有其他问题的话,首先就要考虑采用

  • pymongo给mongodb创建索引的简单实现方法

    本文实例讲述了pymongo给mongodb创建索引的简单实现方法.分享给大家供大家参考.具体如下: 下面的代码给user的user_name字段创建唯一索引 import pymongo mongo = pymongo.Connection('localhost') collection = mongo['database']['user'] collection.ensure_index('user_name', unique=True) 希望本文所述对大家的Python程序设计有所帮助.

  • 关于MongoDB谨防索引seek的效率问题详析

    背景 最近线上的一个工单分析服务一直不大稳定,监控平台时不时发出数据库操作超时的告警. 运维兄弟沟通后,发现在每天凌晨1点都会出现若干次的业务操作失败,而数据库监控上并没有发现明显的异常. 在该分析服务的日志中发现了某个数据库操作产生了 SocketTimeoutException. 开发同学一开始希望通过调整 MongoDB Java Driver 的超时参数来规避这个问题. 但经过详细分析之后,这样是无法根治问题的,而且超时配置应该如何调整也难以评估. 下面是关于对这个问题的分析.调优的过程

  • MongoDB通配符索引的用法实例

    指南 MongoDB在4.2 版本推出了Wildcard Indexes,究竟什么是Wildcard Indexes以及Wildcard Indexes适合哪些场景本文结合官方文档以及实际测试进行简单概述. 1.通配符索引示例 因为MongoDB是dynamic schemas,所以应用是可以查询任何已知字段或者随机字段的. 假设(此假设案例摘自官方文档),集合colA的UserMetadata字段包含如下数据: { "userMetadata" : { "likes&quo

  • MongoDB数据库索引用法详解

    一.索引详讲 索引是什么,索引就好比一本书的目录,当我们想找某一章节的时候,通过书籍的目录可以很快的找到,所以适当的加入索引可以提高我们查询的数据的速度. 准备工作,向MongoDB中插入20000条记录,没条记录都有number和name > for(var i = 0 ; i<200000 ;i++){ ... db.books.insert({number:i,name:"book"+i}) ... } WriteResult({ "nInserted&qu

  • mongoDB数据库索引快速入门指南

    目录 MongoDB 索引 1. 开始与准备数据 2. 创建索引前 3. 创建索引 createIndex 4. 创建索引后 6.唯一索引与符合索引 ①唯一索引 ②复合索引 MongoDB 索引 索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录. 这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的. 索引是特殊的数据结构,索引存储在一个易于遍历读取的数

  • MongoDB的索引

    1.简介 它就像是一本书的目录,如果没有它,我们就需要对整个书籍进行查找来获取需要的结果,即所说的全盘扫描: 而有了目录(索引)之后就可以通过它帮我们定位到目标所在的位置,快速的获取我们想要的结果. 2.演示 第一步,向用户集合users中插入100W条数据 var insertUsers = function() { var start = new Date().getTime(); for (var i = 1; i <= 1000000; i++) { db.users.insert({

  • MongoDB数据库中索引和explain的使用教程

    前言 本文主要给大家介绍了关于MongoDB中索引和explain使用的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: mongodb 索引使用 作用 索引通常能够极大的提高查询. 索引是一种数据结构,他搜集一个集合中文档特定字段的值. B-Tree索引来实现. 创建索引 db.collection.createIndex(keys, options) keys keys由文档字段和索引类型组成.如{"name":1} key 表示字段 value 1,-1 

  • MongoDB中创建索引需要注意的事项

    上周在 ruby-china 上发了帖子<MongoDB 那些坑>,反映相当热烈,许多回复很有见地,其中一位童鞋深入的提到 MongoDB 建索引方法的问题,引发我更深入的了解了 MongoDB 建索引的方法和一些注意事项. 在 <MongoDB 那些坑>中提到,在前台直接运行建立索引命令的话,将造成整个数据库阻塞,因此索引建议使用 background 的方式建立.但是这也会带来一定的问题,在 2.6 版本之前,在 secondary server 中即使使用 backgroun

随机推荐