MongoDB优化浅析

本文涉及的产品
云数据库 MongoDB,通用型 2核4GB
简介: MongoDB 优化

一、MongoDB优化整体思路

  MongoDB的查询语句优化与关系型数据库类似,简单来说就是通过慢查询日志找出慢查询语句,然后通过执行计划进行分析,最后根据实际情况进行优化。

二、慢查询日志分析

2.1 简介

  在MongoDB中,慢查询日志被叫做Profiler,我们可以通过设置Profiler来记录慢查询语句;然后就可以根据慢查询日志中的内容进行优化分析了。
  MongoDB的慢查询日志记录在system.profile这个集合中,默认情况下慢查询日志是关闭的,可以在数据库级别上或者是节点级别上进行配置。

2.2 慢查询日志的使用

  MongoDB有两种方式可以对慢查询日志进行配置:
1、直接在启动参数或者配置文件里进行设置:

#传统配置文件格式
profile = 1
slowms = 100 #默认为100ms
#YAML配置文件格式
operationProfiling:
   mode: <string>  # 默认为 off,可选值 off、slowOp、all
   slowOpThresholdMs: <int> # 阈值,默认值为100ms
   slowOpSampleRate: <double> #随机采集慢查询的百分比值,sampleRate值默认为1,表示都采集,0.5 表示采集50%的内容

2、通过db.setProfilingLevel(level,slowms) 语句设置慢查询级别和阈值;

级别 含义
0 关闭慢查询
1 只记录超过阈值的查询
2 记录所有查询

  MongoDB可以通过db.getProfilingStatus()获取慢查询日志的相关配置信息

#  指定数据库,并指定阈值慢查询 ,超过10毫秒的查询被记录
use test
db.setProfilingLevel(1, { slowms: 10 })
#  随机采集慢查询的百分比值,sampleRate 值默认为1,表示都采集,0.5 表示采集50%的内容。
db.setProfilingLevel(1, { sampleRate: 0.5 }) 
#查询大于等于5ms的时间
db.system.profile.find( { millis : { $gt : 5 } } )
# 查询最近的5个慢查询日志
db.system.profile.find().limit(5).sort( { ts : -1 } )
# 查询除命令类型为'command'的日志
db.system.profile.find( { op: { $ne : 'command' } } )
# 查询数据库为test集合为t1的日志
db.system.profile.find( { ns : 'test.t1' } )
# 查询时间从 2019-09-01 0点到 2012-10-01 0点之间的日志
db.system.profile.find({
  ts : {
    $gt: new ISODate("2019-09-01T00:00:00Z"),
    $lt: new ISODate("2019-10-01T00:00:00Z")
  }
})

2.3 慢查询日志内容详解

{ 
"op" : "query", 
"ns" : "test.data", 
"command" : { "find" : "data", "filter" : { "id" : { "$gt" : 2, "$lt" : 10 }, "name" : "ZJ" }, "lsid" : { "id" : UUID("2fc9dd75-6731-4426-b0ba-38e1a5d7317e") }, "$db" : "test" }, 
"keysExamined" : 0,
 "docsExamined" : 1000000,
 "cursorExhausted" : true,
 "numYield" : 7812, 
 "nreturned" : 7, 
 "queryHash" : "17295960",
 "planCacheKey" : "17295960",
 "locks" : { "ParallelBatchWriterMode" : { "acquireCount" : { "r" : NumberLong(1) } }, "ReplicationStateTransition" : { "acquireCount" : { "w" : NumberLong(7814) } }, "Global" : { "acquireCount" : { "r" : NumberLong(7814) } }, "Database" : { "acquireCount" : { "r" : NumberLong(7813) } }, "Collection" : { "acquireCount" : { "r" : NumberLong(7813) } }, "Mutex" : { "acquireCount" : { "r" : NumberLong(1) } } },
 "flowControl" : {  }, 
 "storage" : {  }, 
 "responseLength" : 546, 
 "protocol" : "op_msg",
 "millis" : 623, 
 "planSummary" : "COLLSCAN",
 "execStats" : { "stage" : "COLLSCAN", "filter" : { "$and" : [ { "name" : { "$eq" : "ZJ" } }, { "id" : { "$lt" : 10 } }, { "id" : { "$gt" : 2 } } ] }, "nReturned" : 7, "executionTimeMillisEstimate" : 2, "works" : 1000002, "advanced" : 7, "needTime" : 999994, "needYield" : 0, "saveState" : 7812, "restoreState" : 7812, "isEOF" : 1, "direction" : "forward", "docsExamined" : 1000000 }, 
 "ts" : ISODate("2019-10-24T04:00:54.221Z"), 
 "client" : "127.0.0.1",
 "appName" : "MongoDB Shell", 
 "allUsers" : [ ], "user" : "" 
 }

  上面是我截取慢查询日志中的一个查询语句,其中可以把它拆解为几个部分来理解:

  • "op"

  该项表明该慢日志的种类,主要包含如下几类:

insert
query
update
remove
getmore
command
  • "ns"

  该项表明该慢日志是哪个库下的哪个集合所对应的慢日志

  • "command"

  该项详细输出了慢日志的具体语句和行为

  • "keysExamined"

  该项表明为了找出最终结果MongoDB搜索了多少个key

  • "docsExamined"

  该项表明为了找出最终结果MongoDB搜索了多少个文档

  • "numYield"

  为了让别的操作完成而屈服的次数,一般发生在需要访问的数据尚未被完全读取到内存中,MongoDB会优先完成在内存中的操作

  • "nreturned"

  该项表明返回的记录数

  • "locks"

  该项表明在操作中产生的锁的相关信息

  • 锁的种类:
级别 含义
Global 全局锁
MMAPV1Journal MMAPV1同步日志时加的一种锁
Database 数据库锁
Collection 集合锁
Metadata 元数据锁
oplog oplog锁
  • 锁的模式:
级别 含义
R 共享S锁
W 排他X锁
r 意向共享IS锁
w 意向排他IX
  • "responseLength"

  结果返回的大小,单位为bytes,该值如果过大,则需考虑limit()等方式减少输出结果

  • "millis"

  该操作从开始到结束耗时多少,单位为ms

  • "execStats"

  包含了该操作执行的详细信息

  • "ts"

  该操作执行时的时间

  • "client"

  哪个客户端发起的该操作,并显示出该客户端的ip或hostname

  • "allUsers"

  哪个认证用户执行的该操作

  • "user"

  是否认证用户执行该操作,如认证后使用其他用户操作,该项为空

三、执行计划分析

3.1 简介

  MongoDB查看执行计划的语法:

db.collection.find().explain() 

  MongoDB执行计划三种模式:

  • queryPlanner Mode:只会显示 winning plan 的 queryPlanner,自建MongoDB默认模式
  • executionStats Mode:只会显示 winning plan 的 queryPlanner + executionStats
  • allPlansExecution Mode:会显示所有执行计划的 queryPlanner + executionStats,阿里云MongoDB默认模式

3.2 执行计划内容详解

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.data",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$and" : [
                {
                    "name" : {
                        "$eq" : "ZJ"
                    }
                },
                {
                    "id" : {
                        "$lt" : 10
                    }
                },
                {
                    "id" : {
                        "$gt" : 2
                    }
                }
            ]
        },
        "queryHash" : "17295960",
        "planCacheKey" : "17295960",
        "winningPlan" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "$and" : [
                    {
                        "name" : {
                            "$eq" : "ZJ"
                        }
                    },
                    {
                        "id" : {
                            "$lt" : 10
                        }
                    },
                    {
                        "id" : {
                            "$gt" : 2
                        }
                    }
                ]
            },
            "direction" : "forward"
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 7,
        "executionTimeMillis" : 650,
        "totalKeysExamined" : 0,
        "totalDocsExamined" : 1000000,
        "executionStages" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "$and" : [
                    {
                        "name" : {
                            "$eq" : "ZJ"
                        }
                    },
                    {
                        "id" : {
                            "$lt" : 10
                        }
                    },
                    {
                        "id" : {
                            "$gt" : 2
                        }
                    }
                ]
            },
            "nReturned" : 7,
            "executionTimeMillisEstimate" : 4,
            "works" : 1000002,
            "advanced" : 7,
            "needTime" : 999994,
            "needYield" : 0,
            "saveState" : 7812,
            "restoreState" : 7812,
            "isEOF" : 1,
            "direction" : "forward",
            "docsExamined" : 1000000
        },
        "allPlansExecution" : [ ]
    },
    "serverInfo" : {
        "host" : "xsj",
        "port" : 27017,
        "version" : "4.2.0",
        "gitVersion" : "a4b751dcf51dd249c5865812b390cfd1c0129c30"
    },
    "ok" : 1
}

  上面这条语句的执行计划可以拆解为几个部分来理解:

  • queryPlanner

  parsedQuery:该部分解析了SQL的所有过滤条件
这个部分很好理解,不在过多说明;
  winningPlan:该部分是SQL最终选择的执行计划
该部分主要关注stage,stage是执行计划的类型,有以下分类:

COLLSCAN:全表扫描
IXSCAN:索引扫描
FETCH:根据索引去检索指定document
SHARD_MERGE:将各个分片返回数据进行merge
SORT:表明在内存中进行了排序
LIMIT:使用limit限制返回数
SKIP:使用skip进行跳过
IDHACK:针对_id进行查询
  • executionStats
    这个部分最好的情况是:nReturned = totalKeysExamined = totalDocsExamined,此部分主要是文档扫描数以及消耗信息。

四、索引分析

4.1 基本命令

  • 查看索引
    MongoDB通过getIndexes()查看集合的所有索引
db.getCollection('data').getIndexes()
[
    {
        "v" : 2,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "test.data"
    },
    {
        "v" : 2,
        "key" : {
            "name" : 1
        },
        "name" : "name_1",
        "ns" : "test.data"
    }
]
  • 查看索引大小
    MongoDB通过totalIndexSize()查看集合索引的总大小
db.getCollection('data').totalIndexSize()
14389248 //单位字节
  • 创建索引
db.collection.createIndex(keys,options)
keys,要建立索引的参数列表。如:{KEY:1/-1},其中key表示字段名,1/-1表示升降序。
options:
    background,在后台建立索引,以便建立索引时不阻止其他数据库活动。默认值 false。
    unique,创建唯一索引。默认值 false。
    name,指定索引的名称。如果未指定,MongoDB会生成一个索引字段的名称和排序顺序串联。
    dropDups,创建唯一索引时,如果出现重复删除后续出现的相同索引,只保留第一个。
    sparse,对文档中不存在的字段数据不启用索引。默认值是 false。
    v,索引的版本号。
    weights,索引权重值,数值在1到99999 之间,表示该索引相对于其他索引字段的得分权重。
  • 删除索引
db.data.dropIndex("user_1")方法用于删除指定的索引
db.data.dropIndexes()方法用于删除全部的索引

4.2 索引分类

  • 单字段索引

  单字段索引是针对单个字段进行设置索引的操作

#创建索引语法
db.data.createIndex({name:1})
name:1代表按照升序进行排序,降序排序的索引为-1
db.data.createIndex({name:-1})
  • 联合索引

  联合索引在单字段索引上进行了多个字段操作,将多个字段合并为一个索引的联合索引,联合索引需要遵守最左前缀原则。

#创建索引语法
db.data.createIndex({name:1,time:1})
  • 多key索引

  当内容是数组或者list集合创建的一种索引。该索引会为数组中的每个字段创建索引。

  • 子文档索引

  该索引用来嵌入子文档中的字段进行创建索引。操作也可以有复合索引,单字段索引。

db.data.createIndex({"user.name":1})

4.2 索引的属性

  Mongodb支持多类型的索引,还能对索引增加一些额外的属性。

唯一索引:在Mongodb中_id就是利用单字段索引加唯一索引的属性构成的
部分索引(3.2版本新增):仅索引符合指定过滤器表达式集合中的文档。部分索引有较低的存储要求,降低索引的创建与维护
稀疏索引:确保索引仅包含具有索引字段的文档的条目,会跳过没有索引字段的文档
TTL索引:在一定时间后自动从集合中删除文档的一种索引

五、总结

  本文介绍了MongoDB优化的基本思路以及如何查看慢日志、执行计划和索引的基本使用,如果有兴趣可以针对性的深入研究。

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
监控 NoSQL 前端开发
软件测试|Mongodb的分页优化及索引使用
软件测试|Mongodb的分页优化及索引使用
378 0
软件测试|Mongodb的分页优化及索引使用
|
NoSQL 关系型数据库 MySQL
MongoDB 慢查询语句优化分析策略
MongoDB查询语句太慢了,开启 Profiling 功能进行分析后发现,问题其实很好解决,涨知识了
447 0
|
1天前
|
缓存 监控 NoSQL
【MongoDB 专栏】MongoDB 的内存管理与优化
【5月更文挑战第11天】MongoDB的内存管理优化对性能至关重要,涉及数据缓存、索引及执行操作的内存使用。动态内存管理根据访问模式和负载调整,可通过配置参数优化,如设置合适缓存大小,调整内存分配参数。索引管理也很重要,需定期评估优化,避免内存占用过高。监控内存使用、数据清理压缩、架构规划也是优化手段。面对挑战,如高并发下的内存不足,需灵活调整策略,平衡系统资源。不断学习新方法,提升内存管理能力,以优化MongoDB性能。
【MongoDB 专栏】MongoDB 的内存管理与优化
|
1天前
|
存储 监控 NoSQL
【MongoDB 专栏】MongoDB 的存储引擎选择与优化
【5月更文挑战第11天】MongoDB 的存储引擎选择与优化至关重要,影响数据库性能、可靠性和可扩展性。常见引擎有默认的 WiredTiger(提供高性能读写、文档级并发控制和压缩)和较旧的 MMAPv1。选择引擎需考虑性能需求、数据规模、并发操作和压缩需求。WiredTiger 以其高性能和并发控制脱颖而出。优化策略包括配置参数、规划数据结构、监控性能和定期维护。案例显示,WiredTiger 对于并发访问频繁的电商平台尤为适合。未来,更高效、智能的存储引擎将应运而生,持续优化将是保持数据库系统竞争力的关键。
【MongoDB 专栏】MongoDB 的存储引擎选择与优化
|
12天前
|
监控 NoSQL MongoDB
MongoDB索引机制与优化策略详解
【4月更文挑战第30天】本文深入解析MongoDB的索引机制,包括单字段、复合、地理空间、全文及哈希索引。介绍了创建与查看索引的方法,并提出了优化策略:选择性创建、使用复合索引、定期审查优化、避免不必要的索引扫描、利用索引前缀与覆盖索引,以及监控索引使用。通过这些策略,可提升MongoDB查询性能。
|
5月前
|
存储 NoSQL MongoDB
MongoDB 助力移动式汽车保养运营模式优化,将开发请求减少 90%
MongoDB针对初级,中级及熟练的技术开发人员推出系列技术文章与行业案例。深入浅出地剖析MongoDB产品基础原理,使用技巧,典型行业场景及应用,还有Code Demo及线上线下活动推荐!
4847 1
MongoDB 助力移动式汽车保养运营模式优化,将开发请求减少 90%
|
5月前
|
存储 监控 NoSQL
MongoDB助力腾讯游戏 优化游戏开发体验
无论在功能还是性能上,MongoDB都很好地契合了游戏业务场景,带给腾讯游戏的不只是功能价值,还有运维价值
MongoDB助力腾讯游戏 优化游戏开发体验
|
9月前
|
NoSQL MongoDB
MongoDB-分片优化
分片的主要目的就是将数据分配到不同的服务器中保存, 提升服务器的容量, 让数据更加的均衡, 更有效的降低服务器的压力, 但是随着时间推移, 某些数据段中保存的数据会越来越多, 所以为了保证个分片均衡, 当某个数据段数据过多或体积过大的时候, 系统就会自动在下一次操作这个数据段时(新增/更新), 将一个大的数据段分裂成多个小的数据段。
126 0
|
10月前
|
存储 Kubernetes NoSQL
Kubernetes在AliCloud上部署并优化MongoDB
Kubernetes, 阿里云, MongoDB, 优化
198 0
|
10月前
|
存储 弹性计算 运维
互娱NoSQL架构优化 —— 暨MongoDB“在线换引擎”技术服务指南”
XX工作室是某大客户核心游戏工作室,其核心业务是国内二次元RPG手游,采用实时开放世界对战模式,整体采用阿里云方案,本次专项攻坚主要对于玩家在游戏期间各类游戏属性交互(包含过图、物品、面板、剧情等)的核心业务模块进行优化,其中涉及NoSQL部分由于在专项优化期间存在诸多细节,特此提炼出来给各位有类似互娱业务场景进行参考。