##查看慢日志
#设置mongo慢查询日志等级和记录时间门槛,单位ms毫秒
db.setProfilingLevel(1,3000)
#查看当前mongo慢查询级别状态,有0,1,2三个级别,0:关闭,不收集任何数据.1:收集慢查询数据,默认是100毫秒.2:收集所有数据
db.getProfilingStatus()
#查看当前mongo慢查询的记录,注意:要查看那个库的慢日志,就要登进去那个库,不然无法显示相应的慢日志
use apptest
db.system.profile.find()
#查看当前mongo慢查询的记录并排序显示最新,只显示10条
use apptest
db.system.profile.find().sort({$natural:-1}).limit(10)
#如果慢查询等级设成了2,可能会记录非常多的日志,就需要按条件过滤了,例如下面:列出执行时间长于某一限度(5ms)的记录
use apptest
db.system.profile.find({millis:{$gt:5}})
#慢日志信息关键项说明

"op" : "query",   # 操作类型,查询query,更改update

"ns" : "test.report", # 操作的数据库和集合

"command" : {   # 执行命令信息

"find" : "report",  # 操作的集合

"filter" : { "a" : { "$lte" : 500 } }, # 查询条件

"docsExamined" : 101, #执行操作而扫描的集合中的文档数.

keysExamined   #索引命中了多少个文档数

keyUpdates    #集合有多少个index key在该操作中被更改

writeConflicts   #写冲突发生的数量,证明有其他语句同时写数据,如果太多会导致大量性能损失

"numYield" : 2,   # 让步次数,操作时让其他的操作完成的次数.MongoDB会优先完成在内存中的操作

nscanned    #扫描的记录数,如果数量比nreturned多很多,可能是索引不合理

"nreturned" : 101,  # 操作返回的文档数

"locks" : {    # 操作期间的锁和所的类型

"Global" : {   #表示全局锁定

"acquireCount" : {  #请求锁定的次数

"r" : NumberLong(3)  # 表示共享锁

"Database" : {   # 数据库锁

"Collection" : {  # 集合锁

"responseLength" : 1305014, # 操作返回结果的文档长度,单位为字节,如果这个值很大,但是nreturned文档不多,就证明这里有大字段

"protocol" : "op_msg", # 消息的协议,分片集群可能会出现op_command

"millis" : 69132,  # 从 MongoDB 操作开始到结束耗费的时间,单位为毫秒

"planSummary" : "IXSCAN { a: 1, _id: -1 }",  #执行计划摘要,不同信息代表不同操作方式

"execStats" : {   # 操作执行过程中的详细信息,有些会嵌套子查询,只有query类型的才会显示

"stage" : "FETCH",  # 操作形式 ,COLLSCAN代表全集合扫描,IXSCAN代表扫描索引键,FETCH代表根据索引检索文档

"ts" : ISODate("2019-01-14T16:57:33.450Z"), #操作的时间戳

"client" : "127.0.0.1", # 客户端的ip

"user" : "someuser", # 如认证后使用其他用户操作,该项为空

allUsers    #属于哪个认证用户执行的该操作

"db" : "admin"   # 验证的数据库

"user" : "someuser@admin" # 经过验证的用户

timestamp    #日期和时间, ISO8601格式

severityLevel   #日志级别 I代表info的意思,其他的还有F,E,W,D等

components    #组件类别,不同组件打印出的日志带不同的标签,便于日志分类

namespace    #查询的命名空间,即<databse.collection>

operation    #操作类别,可能是[find,insert,update,remove,getmore,command]

cursorExhausted   #该项表明本次查询中游标耗尽的次数

reslen     #结果返回的大小,单位为bytes,

queryHash    #查询的hash值

storage     #储存

     bytesRead   #从磁盘放到缓存的数据的字节数

     timeReadingMicros #花费在磁盘读取的时间,以微秒为单位

locks信息细节:

锁的类型:

Global   Represents global lock.

MMAPV1Journal Represents MMAPv1 storage engine specific lock to synchronize journal writes; for non-MMAPv1 storage engines, the mode forMMAPV1Journal is empty.

Database  Represents database lock.

Collection  Represents collection lock.

Metadata  Represents metadata lock.

oplog   Represents lock on the oplog.

锁的模式:

R Represents Shared (S) lock.

W Represents Exclusive (X) lock.

r Represents Intent Shared (IS) lock.

w Represents Intent Exclusive (IX) lock.

stage详细信息列举:

AND_HASH: hash索引交集

AND_SORTED: 普通索引交集

CACHED_PLAN:缓存查询计划

SCAN:  用了索引的去重查询

COLLSCAN: 全集合扫描,最糟糕的方法

IXSCAN:  索引扫描,较优方案

FETCH:  根据索引获取到相关文档

SHARD_MERGE:将各个分片返回数据进行merge

SORT:  表明在内存中进行了排序

LIMIT:  使用limit限制返回文档

SKIP:  使用skip进行跳过文档

IDHACK:  针对_id进行的查询,速度最快

SHARDING_FILTER: 通过mongos对分片数据进行查询

COUNT:  利用db.coll.explain().count()之类进行count运算或者是直接count的计数查询

COUNTSCAN: 未用索引计数,count不使用Index进行时的stage返回

COUNT_SCAN: 使用索引计数,通过索引计数的count,不返回索引值

TEXT:  使用全文索引进行查询时候的stage返回

PROJECTION: 使用索引计数,限定返回字段的查询

DELETE:  数据删除

UPDATE:  数据更改

MULTI_PLAN: 并行的扫描计划

OR:   or的查询

SORT_KEY_GENERATOR: 生成排序用的key

SUBPLA:  未用索引的$or查询阶段,子查询计划,最好用OR,不用子查询


性能较好的Stage状态组合如下:

   Fetch+IDHACK

   Fetch+ixscan,

   Limit+(Fetch+ixscan)

   PROJECTION+ixscan

Components信息细节:

在MongoDB中,不同组件输出的日志会带不同的tag,方便区分,对于慢日志而言,通常都是COMMAND。包含以下:

ACCESS —— 访问控制相关,比如认证

COMMAND —— 数据库命令,CURD等

CONTROL —— 控制行为,比如初始化等

FTDC —— 诊断数据收集机制相关,比如服务器统计信息和状态信息

GEO —— 与解析地理空间形状相关,比如验证GeoJSON形状

INDEX —— 索引操作相关,比如创建索引

NETWORK —— 网络相关,比如链接的建立和断开

QUERY —— 查询相关,比如查询计划

REPL —— 副本集相关,包括初始化同步、副本集节点心跳、主从同步、回滚等

       ELECTION —— 副本集选举相关(属于REPL的子tag)

 INITSYNC —— 初始化同步相关 (属于REPL的子tag)

 REPL_HB —— 副本集内节点心跳相关

 ROLLBACK —— rollback状态相关

   SHARDING —— 分片行为相关,比如mongos的启动

STORAGE —— 存储相关,比如fsync

RECOVERY —— recovery状态相关

JOURNAL —— journal相关

TXN —— 多文档事务相关

operation信息细节:

find、insert、delete、replace、update、drop、rename、dropDatabase


planSummary信息细节:

COLLSCAN —— 全表扫描

IXSCAN —— 索引扫描

IDHACK —— 使用了默认的_id索引

FETCH —— 根据索引去检索某一个文档

SHARD_METGE —— 将各个分片的返回数据进行聚合

SHARDING_FILTER —— 通过mongos对分片数据进行查询

正常情况下如果遇到COLLSCAN导致的慢查询的话,可以考虑新建相应的索引来优化查询。但如果IXSCAN还是慢的话,有可能一个表有多个索引,当这里的索引不符合预期时,也应该考虑优化索引或者通过hint()来改造查询语句。


#通过mongoexport导出慢日志,并生成csv文档,方便查看,参考信息解析如上
mongoexport -h 127.0.0.1 --port 40006 --authenticationDatabase admin -u root -p "****" -d order_service -c system.profile -f op,ns,query,docsExamined,numYield,nreturned,locks,responseLength,protocol,millis,planSummary,execStats,ts,client --type csv  --out=/opt/slow20200706.csv


#mongodb慢查询日志的记录空间默认是只有1MB的固定集合,不利于收集历史信息,但是可以手动设置更大
#先关闭慢查询功能
db.setProfilingLevel(0)
#把旧的固定集合删除
db.system.profile.drop()
#创建一个新的而且空间更大的固定集合空间,这里设置成1GB
db.createCollection("system.profile",{capped:true,size:1073741824})
#重新开启慢查询日志,就更改完成了
db.setProfilingLevel(1)

 

##通过mongodb日志记录查看慢查询日志记录
#有时候mongodb系统自带的system.profile会变得有些鸡肋,看不到慢日志,但是还好,mongodb会主动把慢日志记录到mongodb的日志里面去,这个时候只要我们过滤一下就能看到了
tail -1000000 mongodb_data.log |grep -Ev "NETWORK|ACCESS|connection|COMMAND|ASIO|SHARDING|Error|REPL|background|CONTROL|INDEX">/opt/20210524_43.20.log
2019-11-29T16:11:38.234+0800 I WRITE    [conn2443] update order_service.order query: { _id: "3564-102656377733-5" } exception: [order_service.order] shard version not ok: migration commit in progress for order_service.order ( ns : order_service.order, received : 323|1||5dc1836cdf86ba1d5a9a680e, wanted : 323|1||5dc1836cdf86ba1d5a9a680e, send ) code:13388 numYields:0 locks:{ Global: { acquireCount: { r: 3, w: 1 } }, Database: { acquireCount: { r: 1, w: 1 } }, Collection: { acquireCount: { r: 1, w: 1 } } } 894ms

 

##执行计划

和mysql差不多意思,查询分析可以确保我们建议的索引是否有效,是查询语句性能分析的重要工具,MongoDB 查询分析常用函数有:explain() 和 hint().
#db.collection.explain().<method(...)>
有三个参数选择:queryPlanner,executionStats,allPlansExecution
示例:
    db.users.find({gender:"M"},{user_name:1,_id:0}).explain(executionStats)
这个结果集的字段解析:
winningPlan:            当前步骤最优的执行计划,由系统判断,除非是设置了hint和索引过滤
stage:                    所属步骤的操作类型,有很多种,下面列举
filter:                    查询的过滤条件
inputStage:                所属分支步骤的stage
indexFilterSet:            索引过滤器,如果为true,则说明有相关规则,执行计划只会用这个索引来查询,不管是不是最优计划
namespace:                所属集合的命名空间,也说明了该步骤操作了什么集合
parsedQuery:            每一步骤的详细操作信息
keyPattern:                扫描的index内容
indexName:                使用到索引的索引名称
isMultiKey:                是否是Multikey,即数组类型多key索引
isUnique:                是否唯一索引
isSparse:                是否稀疏索引
isPartial:                是否部分索引
direction:                索引扫描顺序,默认是顺序forward,倒序则是显示backward(即-1)
indexBounds:            扫描索引的边界范围,如果没有指定范围就是[MaxKey, MinKey]
rejectedPlans:            其他执行计划(非最优而被查询优化器reject的)的详细返回
executionStats:            执行状态统计信息
executionSuccess:        是否执行成功
executionTimeMillis:    这个步骤的执行时间,这个值当然是希望越少越好,单位毫秒.
nReturned:                这个步骤返回的结果统计条目数
nCounted:                当stage为COUNT时显示的统计条目数
totalKeysExamined:        这个步骤索引扫描条目数
totalDocsExamined:        这个步骤文档扫描条目数
executionSuccess:        是否执行成功
works:                    查询执行阶段执行的"工作单元"的数量,通常来说一个处理步骤就有一个"工作单元"
advanced:                往下一阶段传递结果的次数,通常和nCounted或nReturned的值相等
needTime:                预估需要更多一些时间才能产生结果并交给下一阶段的数值
needYield:                遇到写冲突在引擎层主动进行等待的次数
saveState:                查询阶段暂停处理并保存其当前执行状态的次数
restoreState:            查询阶段恢复已保存的执行状态的次数
isEOF:                    是否扫描完毕所有的数据
invalidates:            查询过程中发现记录被删除或修改的次数(因为不支持事务)
docsExamined:            在集合里扫描的文档数
alreadyHasObj:            fetch数据中检查发现数据已被获取(数据可能失效或者磁盘位置改变)
seeks:                    为完成索引扫描,必须将索引游标定位到新位置的次数
dupsTested:                在使用数组索引中出现,检测是否有重复文档
dupsDropped:            处理重复扫描记录的次数
seenInvalidated:        再次看到并处理之前扫描过的记录的次数

nscanned                扫描的记录数,如果数量很多,可能是索引不合理
millis                    查询开始到结束耗费的时间,耗费越多时间,就证明这个语句越慢,需要优化
n                        本次查询返回的文档数量
nscannedobjects            按照索引查找到文档的次数,有些可能是使用多次,有些可能是不经过索引查找,所以和n值并不一定相等
scanandorder            是否在内存中对结果进行了排序,这个当然是比较耗费资源的
indexonly                是否直接用索引就查到数据,即直接使用了最高效的覆盖索引
nyields                    本次查询暂停的次数,因为monogodb不是事务型数据库,每次写入都会暂停查询,如果过于频繁暂停,查询也会慢

 

stage详细信息列举:
    AND_HASH:    hash索引交集
    AND_SORTED:    普通索引交集
    CACHED_PLAN:缓存查询计划
    SCAN:        用了索引的去重查询
    COLLSCAN:    全集合扫描,最糟糕的方法
    IXSCAN:        索引扫描,较优方案
    FETCH:        根据索引获取到相关文档
    SHARD_MERGE:将各个分片返回数据进行merge
    SORT:        表明在内存中进行了排序
    LIMIT:        使用limit限制返回文档
    SKIP:        使用skip进行跳过文档
    IDHACK:        针对_id进行的查询,速度最快
    SHARDING_FILTER:    通过mongos对分片数据进行查询
    COUNT:        利用db.coll.explain().count()之类进行count运算或者是直接count的计数查询
    COUNTSCAN:    count不使用Index进行count时的stage返回
    COUNT_SCAN:    通过索引计数的count,不返回索引值
    TEXT:        使用全文索引进行查询时候的stage返回
    PROJECTION:    限定返回字段的查询
    DELETE:        数据删除
    UPDATE:        数据更改
    MULTI_PLAN:    并行的扫描计划
    OR:            or的查询
    SORT_KEY_GENERATOR:    生成排序用的key
    SUBPLAN:    子查询计划,最好用OR,不用子查询
    
    
#hint()
和mysql一样,虽然MongoDB查询优化器一般工作的很不错,但是某种方法在某些情形下用另外一些索引或字段来做条件会提升性能,那我们就可以使用 hint 来强制 MongoDB 使用一个指定的索引.
    db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1})

函数解析:
$natural:    强制数据库做全集合扫描

    
索引过滤器
指定查询指纹选择对应的索引,如果某个特定查询指纹存在索引过滤器,则优化器只会选择索引过滤器指定的索引,并自动忽略hint()和其他执行计划.
db.runCommand({
planCacheSetFilter:<集合名>,
query:<语句>,
sort:<排序>,
projection:<限制返回条件>,
indexes:[<索引1>,<索引2>,...]
})
其中:
    query:        就是查询语句本身
    sort:        排序子句,没有也可以不指定
    projection:    相当于mysql的where子句,没有也可以不指定
    indexes:    就是指定用的索引    
    

##优化方案

   对于无索引覆盖的查询,则需要根据查询条件创建对应索引.

   对于点查可以创建hash索引.

   对于多字段查询,单字段重复度较高的场景,创建复合索引.

   对于范围查找,结果集有序的查询,创建升序或者降序索引.

   对于复合索引,由于复合索引是前缀排序查询,所以查询条件的顺序要与索引字段的顺序一致.

   对于分区集合(表)、大集合(超过10万记录),不要直接对大数据量的表使用模糊查询(即不能使用like).这样会造成查询扫描的记录过多,建议先基于索引字段查询,过滤出较小的数据集后再使用模糊查询.

   避免使用$not.MongoDB 并不会对缺失的数据进行索引,因此$not的查询条件将会要求在一个结果集中扫描所有记录.如果$not是唯一的查询条件,会对集合进行全表扫描.

   用$and时把匹配最少结果的条件放在最前面,用$or时把匹配最多结果的条件放在最前面.

   检查实例规格的性能基线,分析当前的业务需求是否达到上限,如已达当前实例性能瓶颈,请及时提升实例规格.



##日志分析工具mtools
mtools 是由MongoDB 官方工程师实现的一套工具集,可以很快速的日志查询分析.统计功能,此外还支持本地集群部署管理,非常便于新手学习.
现在我们只针对他的日志分析功能,因为他不需要结合实时业务,可以安装在任何机器上,只分析mongodb日志文件.所以把mongodb日志下载下来用它分析就可以,但是必须在python3下执行.
#安装:因为不需要关心装在那台机上,直接先安装pip3,再通过pip安装,简单便捷,还有相关依赖包
    apt-get install -y pip3
    apt-get install -y python3-tk
#安装mtools的py包
    pip3 install mtools
#安装2D图表渲染模块,图形生成工具包matplotlib,安装后可以生成图表样式,非常方便
    pip3 install matplotlib
#其他依赖包,通常这些包会被当依赖包一早装上
    #用于管理进程的工具
    pip3 install psutil
    #mongodb python驱动
    pip3 install pymongo
    #支持科学计算的工具
    pip3 install numpy

#命令汇总
mlaunch:支持快速搭建本地测试环境,可以是单机.副本集.分片集群.
mlogfilter:日志过滤组件,支持按时间检索慢查询.全表扫描操作,支持通过多个属性进行信息过滤,支持输出为JSON格式.
mplotqueries:支持将日志分析结果转换为图表形式,依赖于tkinter(python图形模块).和matplotlib模块.
mlogvis:支持将日志分析结果转换为一个独立的HTML页面,实现与mplotqueries同样的功能.
mloginfo:用于做日志信息统计的工具

#示例
mloginfo:
日志的起止时间范围.主机端口.版本.数据库引擎等概要信息
mloginfo mongo.log
检查客户端的连接数情况
mloginfo mongo.log --connections
望统计出当前某些事件的发生频次
mloginfo mongo.log --distinct
分析慢查询
mloginfo mongo.log --queries --sort count
重启信息
mloginfo mongo.log --restart
副本集切换
mloginfo mongo.log --rsstate

mlogfilter:
查看超过10s的慢操作
mlogfilter mongo.log --slow 10000 --shorten 200
查看慢扫描操作
mlogfilter mongo.log --scan --shorten 200
根据名称空间过滤
mlogfilter mongo.log --namespace dmspace.DeviceInfo
根据操作类型过滤
mlogfilter mongo.log --operation update
获取某时间点之后1小时的日志
mlogfilter mongo.log --from Apr 6 0:00 --to "+1h" | tail -n3
时区转换
mlogfilter mongo.log --tiemzone 8 > mongo-correct.log

mplotqueries:将日志中扁平的文字信息转换为图表形式
按操作分组输出的散点图
mplotqueries mongo.log --group operations --output-file operations.png
按名称空间进行分组(限显示20个),y轴为nscanned值,即扫描记录数量
mplotqueries mongo.log --group namespace --group-limit 20 --type scatter --yaxis nscanned --output-file namespace_nscan.png
每小时,每个集合的操作比例分布,此时可以采用直方图
mplotqueries mongo.log --group namespace --bucketsize 3600 --group-limit 10 --type histogram --output-file namespaces_perhour.png
连接变更统计类型
mplotqueries mongo.log --type connchurn --bucketsize 3600 --output-file connchurn_perhour.png
扫描数/返回数
mplotqueries mongo.log --type nscanned/n --group-limit 20 --output-file nscan.png
    
mlogvis:直接生成html页面,内置强大的脚本,基本上覆盖了mplotqueries的绝大多数图表功能.
mlogvis mongo.log