项目刚上线,流量突然变大,随之而来的是不断的异常日志,经排查,发现有一处的 Mongo 查询为 9 秒多。
使用 Profiling
设置
db.setProfilingLevel(level, slowms)
level
级别,0 : 关闭不收任何数据,1 : 收集慢查询,默认为 100 ms,2 : 收集所有数据。slowms
当超过时,收集数据。
因为 profile 会影响到一定的性能,所以推荐 level 为 1,我这边设为 1 500。
返回:was
当前的 profiling 等级slowms
记录的毫秒数
确认配置
db.getProfilingStatus()
其他配置方式
- 在 MongoDB 启动的时候设置:
mongod --profile 1 --slowms 500
- 在配置文件中:
1
2profile = 1
slowms = 200
重置
profile 默认的大小为 1 MB,如果需要更大的空间,可以自己进行配置。1
2
3
4db.setProfilingLevel(0)
db.system.profile.drop()
db.createCollection( "system.profile", { capped: true, size:4000000 } )
db.setProfilingLevel(1)
其他
profile 和 oplog 一样,都是 capped collection
,即在一定大小内,会将新的数据覆盖旧的数据。详见
分析
查询
db.system.profile.find().pretty()
查询的过滤条件和普通查询一致,比如:1
2db.system.profile.find( { op: { $ne : ‘command‘ } }).pretty()
db.system.profile.find().sort({ millis: {gte: 1000 }}).pretty()
返回结果,具体可以查看 Database Profiler Output1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29{
"op" : "update", //操作,有 command,count,distinct,geoNear 等
"ns" : "test.test1", //操作的表
"query" : {}, //查询
"updateobj" : {}, //在更新操作时会有该字段,超过 50 kb 时,其余会用 ... 代替
"keysExamined" : 9998, //之前为 nscanned 字段,代表数据操作时,扫描过的 key
"locks" : { //锁相关信息
"Global" : { //全局锁
"acquireCount" : { //操作时请求获取锁的次数
"r" : NumberLong(79),
"w" : NumberLong(79)
}
},
"Database" : {
"acquireCount" : {
"w" : NumberLong(79)
}
},
"Collection" : { //集合锁
"acquireCount" : {
"w" : NumberLong(79)
}
}
},
"millis" : 104,
"ts" : ISODate("2017-07-27T14:20:24.117+08:00"),
"client" : "",
"user" : ""
}
注:锁相关资料可以查看 FAQ
优化
- 若为查询语句,可以先观察
keysExamined
字段,如果过大,可以适当调整当前集合的索引。 - 聚合查询,不知道是业务较复杂,还是当初设计时候未留有较大的空间,较多的使用
aggregation
的查询会使用到$lookup
,可以适量通过冗余,以空间换时间不是个坏方法,另外在pipe
的时候尽量只提取需要的字段。 - 负载均衡,读写分离。
- 有一个笨方法,在提交代码之前,可以在本地使用
db.xxx.explain()
,先分析性能,再进行优化提交。
这篇文章仅仅是基础,在上线之后的更多经验,愿以后可以慢慢分享。