美文网首页
MongoDB学习报告(二)

MongoDB学习报告(二)

作者: Mr_Lance | 来源:发表于2016-07-24 22:08 被阅读193次

概述


MongoDB索引管理
MongoDB查询优化

MongoDB索引管理

  • 单键索引中的每一项都应该对应被索引文档里面的一个值
  • 复合索引就是每一项都由多个键组合而成的索引。复合索引里的键的顺序是很重要。
  • 每一个索引都会对维护带来成本,如果一个集合上有10个索引,每次写操作都要更新10个索引,所以要保证没有多余的索引
  • 使用索引起码要保证索引都放到内存中,理想状态是索引和使用中的集合都放到内存里面。
  • <b style=color:red>一个集合可以创建多个索引,但是只会使用最合适的一个,$or例外(待明确)</b>

索引管理

<b style=color:#87CEFA>创建索引:</b>索引的创建方法3.0之前使用ensureInde()方法,3.0之后使用的是createIndex(),1为指定按升序创建索引,如果你想按降序来创建索引指定为-1。如果文档中嵌套文档,可以建立子文档的索引。对应大数据集合,构建索引可能会需要几个小时或者几天,background项允许索引创建在后台执行。

#单键索引,用户表为例。
db.user.createIndex({name: 1});
#复合索引
db.user.createIndex({name: 1,age: -1});
#输出显示,索引创建成功
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}
#子文档索引,假如用户的地址address是一个文档,可以以address的city值建立索引
db.user.createIndex({address.city: 1});
#后台执行索引创建
db.user.createIndex({name: 1,age: -1},{background: true});

<b style=color:#87CEFA>查询索引:</b>

 db.user.getIndexes();
#结果
[
    {
        "v" : 1,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "test.user"
    },
    {
        "v" : 1,
        "unique" : true,
        "key" : {
            "name" : 1
        },
        "name" : "name_1",
        "ns" : "test.user"
    }
]

<b style=color:#87CEFA>删除索引:</b>dropIndex()删除指定索引,dropIndexes()删除全部索引,_id除外。

#删除索引名为name_1的索引
db.user.dropIndex("name_1");
#删除所有索引
db.user.dropIndexes();

<b style=color:#87CEFA>唯一索引:</b>创建方式,设置unique。唯一索引通常插入数据之前创建,提前创建避免数据重复导致唯一索引建立失败,如果数据不重要,可以使用dropDups选项告诉数据库自动删除重复文档。

#设置name键唯一索引,
db.user.createIndex({name: 1},{unique: true});
#设置唯一索引后插入重复数据会有一个错误提示
WriteResult({
    "nInserted" : 0,
    "writeError" : {
        "code" : 11000,
        "errmsg" : "E11000 duplicate key error collection: test.user index: name_1 dup key: { : \"lance\" }"
    }
})
#已有重复数据的集合创建索引
db.user.createIndex({name: 1},{unique: true,dropDups: true});

<b style=color:#87CEFA>稀疏索引:</b>索引默认都密集型的,就是说一个有索引的集合中,每一个文档都有对应的索引项,如果文档没有索引键,对应的索引也会有个null的值。例如上面的用户表中,有的用户没有age的键,假如创建一个age的索引,* db.user.find({age: null}) *查询也是可以使用到索引定位到对应的用户。但是有些情况下密集型索引并不适合,例如:

  • 商城的产品集合都有个sku的唯一码,如果sku键有唯一索引,现在要插入多个没有sku的产品,那么第一次会成功,之后的都会失败,因为唯一索引里sku已经有了null的项。这种情况下就需要稀疏索引。
db.product.createIndex({sku: 1},{unique: true, sparse: true})
  • 评论功能user_id键建立索引,但是允许匿名评论。这种情况下如果使用密集型,因为集合中大量的文档user_id的键不存在,索引就会拥有大量的null的项都会是null,这样会增加了索引的大小,更重要的是user_id为null的文档写操作的时候也会更新索引。极少或者不对匿名用户进行查询的情况下应该使用稀疏索引。
db.commons.createIndex({user_id}: 1},{sparse: true})

<b style=color:#87CEFA>多键索引:</b>MongoDB允许索引中的多个条目指向同一个文档,多键索引默认处于激活状态,只要被索引键为数组每个数组值都会在索引中有自己的位置。例如文章集合中每篇文章可能有多个标签,用数组tags记录。假如在tags键上建立索引,则tags每个数组值都会在索引中。

{
    "title":文章列表,
    "tags":["mysql", "nosql", "nginx"]    
}

MongoDB查询优化

<b style=color:#87CEFA>慢查询:</b>开启可以有效的识别语句消耗是否超过预期。Mongodb开启慢查询使用的是Profiling工具,该工具在运行的实例上收集有关MongoDB的写操作,游标,数据库命令等,可以在数据库级别开启该工具,也可以在实例级别开启。该工具会把收集到的所有都写入到system.profile<b style=color:red>固定集合</b>,集合大小是固定的,超过最大默认值时新记录覆盖旧记录。

  • 可以修改配置文件默认开启慢查询
#打开配置文件,添加慢查询配置。profile是开启的级别,0是关闭;1是收集慢查询数据,开启慢查询;2是记录所有的操作。slowms慢查询时间,单位毫秒,默认100ms
profile=1
slowms=200
  • 直接在对应的数据库执行开启命令
#开启慢查询,记录执行时间超过200ms的查询语句。如果不填则默认100ms。
db.setProfilingLeve(1,200);
#输出效果
{ "was" : 0, "slowms" : 200, "ok" : 1 } 
#可以查询当前数据开启的profile
db.getProfilingLeve();
#查询用户表
db.user.find();
#查看慢system.profile集合,pretty格式化输出
db.system.profile.find().pretty()
{
    "op" : "query", #操作类型,有insert、query、update、remove、getmore、command   
    "ns" : "test.user", #操作的集合
    "query" : {  #查询语句
        "find" : "user",
        "filter" : {
            
        }
    },
    "keysExamined" : 0,#索引扫描条目,对应3.2版本之前的nscanned
    "docsExamined" : 2, #文档扫描条目,对应3.2版本之前的nscannedObjects
    "cursorExhausted" : true,
    "keyUpdates" : 0,#在操作里更新的改变 index 的数量。改变一个索引值会消耗一点性能,因为数据库必须移掉老的值并插入新的值到B-tree索引中。
    "writeConflicts" : 0,
    "numYield" : 0,
    "locks" : {  #锁信息,R:全局读锁;W:全局写锁;r:特定数据库的读锁;w:特定数据库的写锁
        ......
    },
    "nreturned" : 2,  #返回的记录数。如果用limit(n)命令将返回n个文档,ntoreturn值是n
    "responseLength" : 314,  #结果字节长度,一个大的 responseLength会影响性能.
    "protocol" : "op_command",
    "millis" : 106,    #消耗的时间(毫秒)
    "execStats" : {
        ......
    },
    "ts" : ISODate("2016-07-22T07:32:46.289Z"),#语句执行的时间
    "client" : "127.0.0.1",#链接ip或则主机
    "allUsers" : [
        {
            "user" : "lance",
            "db" : "admin"
        }
    ],
    "user" : "lance@admin" #用户
}

#执行带索引的搜索,"millis" : 13,keysExamined和docsExamined都变成1。
db.user.find({name:"lance"})

<b style=color:#87CEFA>分析慢查询:</b>
** 使用并了解EXPLAIN() **

  • 3.0版本之前,可以直接用explain()函数
  • 3.0版本后explain有所改动,分成三种模式,默认queryPlanner,executionStats,allPlansExecution
    • queryPlanner查询计划的选择器,首先进行查询分析,最终选择一个winningPlan,是explain返回的默认层面。
    • executionStats为执行统计层面,返回winningPlan的统计结果
    • allPlansExecution为返回所有执行计划的统计,包括rejectedPlan
db.test.find({name:'lance'}).explain()
#结果
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.test", #该值返回的是该query所查询的表
        "indexFilterSet" : false, #该query是否有indexfilter
        "parsedQuery" : {
            "name" : {
                "$eq" : "lance"
            }
        },
        "winningPlan" : {  #查询优化器针对该query所返回的最优执行计划的详细内容
            "stage" : "FETCH",#最优执行计划的stage,这里返回是FETCH,可以理解为通过返回的index位置去检索具体的文档(下面有不同类型的介绍)
            "inputStage" : { #用来描述子stage,并且为其父stage提供文档和索引关键字。
                "stage" : "IXSCAN", 
                "keyPattern" : {
                    "name" : 1  #所扫描的index内容
                },
                "indexName" : "name_1",
                "isMultiKey" : false, #是否是Multikey,此处返回是false,如果索引建立在array上,此处将是true
                ......
                "direction" : "forward", #此query的查询顺序,此处是forward,如果用了.sort({modify_time:-1})将显示backward
                "indexBounds" : { #所扫描的索引范围,如果没有制定范围就是[MaxKey, MinKey],这主要是直接定位到mongodb的chunck中去查找数据,加快数据读取。
                    "name" : [
                        "[\"lance\", \"lance\"]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ] #其他执行计划(非最优而被查询优化器reject的)的详细返回
    },
    ......
}

db.test.find({name:'lance'}).explain('executionStats')
{
    "queryPlanner" : {
        ...... 
    },
    "executionStats" : {
        "executionSuccess" : true, #是否执行成功
        "nReturned" : 1, #满足查询条件的文档个数,即查询的返回条数
        "executionTimeMillis" : 0, #整体执行时间
        "totalKeysExamined" : 1, #索引整体扫描的文档个数
        "totalDocsExamined" : 1,#document扫描个数
        "executionStages" : {
            ......
        }
    },
    ......
}

** stage的类型的意义 **

  • COLLSCAN :全表扫描
  • IXSCAN:索引扫描
  • FETCH::根据索引去检索指定document
  • SHARD_MERGE:各个分片返回数据进行merge
  • SORT:表明在内存中进行了排序(与前期版本的scanAndOrder:true一致)
  • SORT_MERGE:表明在内存中进行了排序后再合并
  • LIMIT:使用limit限制返回数
  • SKIP:使用skip进行跳过
  • IDHACK:针对_id进行查询
  • SHARDING_FILTER:通过mongos对分片数据进行查询
  • COUNT:利用db.coll.count()之类进行count运算
  • COUNTSCAN:count不使用用Index进行count时的stage返回
  • COUNT_SCAN:count使用了Index进行count时的stage返回
  • SUBPLA:未使用到索引的$or查询的stage返回
  • TEXT:使用全文索引进行查询时候的stage返回

相关文章

  • MongoDB学习报告(二)

    概述 MongoDB索引管理MongoDB查询优化 MongoDB索引管理 单键索引中的每一项都应该对应被索引文档...

  • MongoDB学习(二)

    MongoDB 更新文档 MongoDB 使用update()和save()方法来更新集合中的文档。 update...

  • MongoDB学习记录(二)

    MongoDB学习记录(二) MongoDB的查询 基本查询语句 基本的查询语句的语法:db.[集合名].fin...

  • mongodb学习系列(二)

    安装及其使用 1.安装 Mongodb也分为两端: 1)服务器端 mongod.exe 2)客户端 mongo....

  • MongoDB学习笔记(二)

    MongoDB的模型设计方法论 虽然说mongoDB不像传统的关系型数据库,没有固定的schema,但是在项目中实...

  • Mongodb 学习笔记(二)

    日常的增删查改 1. 增: insert 介绍: mongodb 存储的是文档,文档是json格式的对象。语法: ...

  • Mac MongoDB学习(二)

    db.xxx.find使用 在MySql,会对>大于),<(小于),=(等于)这些东西很熟悉,但是在db中会有区别...

  • MongoDB学习笔记(二)

    分片集群(Sharded Cluster) 分片(sharding)是一种跨多台机器分布数据的方法, MongoD...

  • NoSQL三--mongodb(一)

    目录 一、mongodb介绍二、mongodb安装三、连接mongodb四、mongodb用户管理五、mongod...

  • MongoDB索引二(九)

    MongoDB索引二(九) 接上篇MongoDB索引一

网友评论

      本文标题:MongoDB学习报告(二)

      本文链接:https://www.haomeiwen.com/subject/mgivjttx.html