mongodb优化

优化服务器硬件

mongodb内存使用方式

  1. mongodb使用内存映射文件I/O访问存储文件,系统将按需把需要的文件映射到RAM中。
  2. 工作集大小。这个大小代表着mongodb实例中存储的数据量。对大多数mongodb实例而言,常规操作通常只会访问到其中一部分数据。了解工作集大小可以帮助正确计算硬件大小。
  3. 确定硬件是最需要注意的是RAM的大小。不需要在服务器上安装512G的内存,因为可以通过分片将数据分散到多台服务器。

评估查询性能

mongodb有两个用于优化查询性能的工具:

  • mongodb分析器:查找查询性能不佳的查询以及选择查询用于进一步检查的好工具。
  • explain():研究单个查询,通过explain()可以判断查询的执行性能。

    mongodb分析器

    分析器级别

    MongoDB数据库分析器可以在实例级别打开,也可以在数据库级别打开。
    MongoDB数据库分析器可以设置三个级别:

  • 0 - 分析器处在关闭状态,不收集任何数据
  • 1 - 仅仅收集较慢操作的分析数据。默认情况下如果一个操作花费的时间超过100ms,就认为是较慢的操作。
  • 2 - 收集所有数据库操作的分析数据。

    system.profile集合

    MongoDB数据库分析器收集的数据将存放在system.profile集合中。system.profile是一个固定大小的集合,数据将循环写入该集合。当用完所有分配的空间后,MongoDB将会覆盖集合中最老的文档。默认情况下,system.profile集合大小为4M。开启分析器后,对应的数据库下创建system.profile集合,可以通过以下方式查看该集合信息:

> db.system.profile.stats()
{
    "ns" : "test.system.profile",
    "size" : 0,
    "count" : 0,
    "storageSize" : 4096,
    "capped" : true,
    "max" : -1,
    "maxSize" : 1048576,
    "sleepCount" : 0,
    "sleepMS" : 0,
    "wiredTiger" : {
        "metadata" : {
            "formatVersion" : 1
        },
        .......
}

启用和禁用mongodb分析器

启用(数据库级别)

> use test
switched to db test
> db.setProfilingLevel(1)
{ "was" : 0, "slowms" : 100, "ok" : 1 }
  • “slowms”:100 指超过100ms的查询都被列为慢查询

禁用 (数据库级别)

db.setProfilingLevel(0)
{ "was" : 1, "slowms" : 100, "ok" : 1 }

指定特定时间的查询启用分析器

> use test
switched to db test
> db.setProfilingLevel(1,500)
{ "was" : 0, "slowms" : 100, "ok" : 1 }
> db.getProfilingStatus()
{ "was" : 1, "slowms" : 500 }

查看分析器Level

> db.getProfilingLevel()
1

查找慢查询

db.system.profile.find()

{
    "op" : "query",
    "ns" : "test.col",
    "query" : {
        "find" : "col",
        "filter" : {

        }
    },
    "keysExamined" : 0,
    "docsExamined" : 2,
    "cursorExhausted" : true,
    "numYield" : 0,
    "locks" : {
        "Global" : {
            "acquireCount" : {
                "r" : NumberLong(2)
            }
        },
        "Database" : {
            "acquireCount" : {
                "r" : NumberLong(1)
            }
        },
        "Collection" : {
            "acquireCount" : {
                "r" : NumberLong(1)
            }
        }
    },
    "nreturned" : 2,
    "responseLength" : 165,
    "protocol" : "op_command",
    "millis" : 0,
    "planSummary" : "COLLSCAN",
    "execStats" : {
        "stage" : "COLLSCAN",
        "nReturned" : 2,
        "executionTimeMillisEstimate" : 0,
        "works" : 4,
        "advanced" : 2,
        "needTime" : 1,
        "needYield" : 0,
        "saveState" : 0,
        "restoreState" : 0,
        "isEOF" : 1,
        "invalidates" : 0,
        "direction" : "forward",
        "docsExamined" : 2
    },
    "ts" : ISODate("2016-12-27T05:35:27.941Z"),
    "client" : "127.0.0.1",
    "appName" : "MongoDB Shell",
    "allUsers" : [ ],
    "user" : ""
}

查找执行时间大于某个特定时间的查询

db.system.profile.find({millis:{$gt:10}}).sort({millis:-1})

system.profile只是一个普通的集合,在普通集合的查询方式在此都可用。可结合更多的字段进行筛选。

增大分析集合的大小

顺序如下:
1. 禁用目标数据库上的分析器

 db.setProfilingLevel(0)
  1. 删除现有的system.profile集合
 db.system.profile.drop()
  1. 创建新的分析器集合system.profile,并使用目标字节大小作为参数。通过db.system.profile.stats()命令可以看到”capped” : true,说明该集合是一个固定大小的集合。
db.createCollection('system.profile',{capped:true,size:50*1024*1024})
  1. 成功创建集合后,重新启用分析器
db.setProfilingLevel(1)

使用explain分析特定的查询

插入测试数据


>use test
switched to db test
> for (var i=1;i<=10000;i++) db.col.insert({a:i,b:i+1})
WriteResult({ "nInserted" : 1 })

执行计划分析详解

现版本explain有三种模式,分别如下:

  1. queryPlanner
  2. executionStats
  3. allPlansExecution
queryPlanner

queryPlanner是现版本explain的默认模式,queryPlanner模式下并不会去真正进行query语句查询,而是针对query语句进行执行计划分析并选出winning plan。

> db.test.find({a:{$gt:8000}}).explain()
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.test",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "a" : {
                "$gt" : 8000
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "a" : 1
                },
                "indexName" : "a_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "a" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "a" : [
                        "(8000.0, inf.0]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "serverInfo" : {
        "host" : "iz2zecqalqk7c59mfkog6gz",
        "port" : 27017,
        "version" : "3.4.1",
        "gitVersion" : "5e103c4f5583e2566a45d740225dc250baacfbd7"
    },
    "ok" : 1
}

关键字段解释:
1. explain.queryPlanner:queryPlanner的返回
2. explain.queryPlanner.namespace:该值返回的是该query所查询的表名
3. explain.queryPlanner.indexFilterSet:针对该query是否有indexfilter
4. explain.queryPlanner.winningPlan:查询优化器针对该query所返回的最优执行计划的详细内容
5. explain.queryPlanner.winningPlan.stage:最优执行计划的stage,这里返回是FETCH,可以理解为通过返回的index位置去检索具体的文档
如下几类介绍

COLLSCAN:全表扫描
IXSCAN:索引扫描
FETCH:根据索引去检索指定document
SHARD_MERGE:将各个分片返回数据进行merge

  1. explain.queryPlanner.winningPlan.inputStage:explain.queryPlanner.winningPlan.stage的child stage,此处是IXSCAN,表示进行的是index scanning
  2. explain.queryPlanner.winningPlan.keyPattern:所扫描的index内容,此处是a:1
  3. explain.queryPlanner.winningPlan.indexName:winning plan所选用的index
  4. explain.queryPlanner.winningPlan.isMultiKey:
    是否是Multikey,此处返回是false,如果索引建立在array上,此处将是true
executionStats
> db.test.find({a:{$gt:8000}}).explain('executionStats')
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.test",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "a" : {
                "$gt" : 8000
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "a" : 1
                },
                "indexName" : "a_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "a" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "a" : [
                        "(8000.0, inf.0]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1999,
        "executionTimeMillis" : 5,
        "totalKeysExamined" : 1999,
        "totalDocsExamined" : 1999,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 1999,
            "executionTimeMillisEstimate" : 10,
            "works" : 2000,
            "advanced" : 1999,
            "needTime" : 0,
            "needYield" : 0,
            "saveState" : 15,
            "restoreState" : 15,
            "isEOF" : 1,
            "invalidates" : 0,
            "docsExamined" : 1999,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 1999,
                "executionTimeMillisEstimate" : 0,
                "works" : 2000,
                "advanced" : 1999,
                "needTime" : 0,
                "needYield" : 0,
                "saveState" : 15,
                "restoreState" : 15,
                "isEOF" : 1,
                "invalidates" : 0,
                "keyPattern" : {
                    "a" : 1
                },
                "indexName" : "a_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "a" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "a" : [
                        "(8000.0, inf.0]"
                    ]
                },
                "keysExamined" : 1999,
                "seeks" : 1,
                "dupsTested" : 0,
                "dupsDropped" : 0,
                "seenInvalidated" : 0
            }
        }
    },
    "serverInfo" : {
        "host" : "iz2zecqalqk7c59mfkog6gz",
        "port" : 27017,
        "version" : "3.4.1",
        "gitVersion" : "5e103c4f5583e2566a45d740225dc250baacfbd7"
    },
    "ok" : 1
}

executionStats.executionSuccess:是否执行成功

executionStats.nReturned:查询的返回条数

executionStats.executionTimeMillis:整体执行时间

executionStats.totalKeysExamined:索引扫描次数

executionStats.totalDocsExamined:document扫描次数

executionStats.executionStages.stage:这里是FETCH去扫描对于documents

executionStats.executionStages.nReturned:由于是FETCH,所以这里该值与executionStats.nReturned一致

executionStats.executionStages.docsExamined:与executionStats.totalDocsExamined一致

allPlansExecution

allPlansExecution模式是将所有的执行计划均进行executionStats模式的操作

对Explain返回逐层分析
第一层,executionTimeMillis

首先,最为直观explain返回值是executionTimeMillis值,指的是我们这条语句的执行时间,这个值当然是希望越少越好

有3个executionTimeMillis,分别是:

  1. executionStats.executionTimeMillis
    该query的整体查询时间
  2. executionStats.executionStages.executionTimeMillis
    该查询根据index去检索document获取具体数据的时间
  3. executionStats.executionStages.inputStage.executionTimeMillis
    该查询扫描index所用时间
第二层,index与document扫描数与查询返回条目数

这里主要谈3个返回项,nReturned,totalKeysExamined与totalDocsExamined,分别代表该条查询返回的条目、索引扫描条目和文档扫描条目。

很好理解,这些都直观的影响到executionTimeMillis,我们需要扫描的越少速度越快。

对于一个查询, 我们最理想的状态是

nReturned=totalKeysExamined & totalDocsExamined=0
(cover index,仅仅使用到了index,无需文档扫描,这是最理想状态。)

或者

nReturned=totalKeysExamined=totalDocsExamined(需要具体情况具体分析)
(正常index利用,无多余index扫描与文档扫描。)

如果有sort的时候,为了使得sort不在内存中进行,我们可以在保证nReturned=totalDocsExamined的基础上,totalKeysExamined可以大于totalDocsExamined与nReturned,因为量级较大的时候内存排序非常消耗性能。

第三层,Stage状态分析

stage的类型:

COLLSCAN

IXSCAN

FETCH

SHARD_MERGE

SORT

LIMIT

SKIP

IDHACK

SHARDING_FILTER

COUNT

COUNTSCAN

COUNT_SCAN

SUBPLA

TEXT

PROJECTION

对于普通查询,我们最希望看到的组合有这些:

Fetch+IDHACK

Fetch+ixscan

Limit+(Fetch+ixscan)

PROJECTION+ixscan

SHARDING_FILTER+ixscan

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

COUNT_SCAN

不希望看到的有:

COUNTSCAN

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值