mongodb可以通过profile来监控数据,进行优化。
查看当前是否开启profile功能用命令
db.getProfilingLevel() 返回level等级,值为0|1|2,分别代表意思:0代表关闭,1代表记录慢命令,2代表全部
开始profile功能为
db.setProfilingLevel(level); #level等级,值同上
level为1的时候,慢命令默认值为100ms,更改为db.setProfilingLevel(level,slowms)如db.setProfilingLevel(1,50)这样就更改为50毫秒
通过db.system.profile.find() 查看当前的监控日志。
如:
这里值的含义是
ts:命令执行时间
info:命令的内容
query:代表查询
order.order: 代表查询的库与集合
reslen:返回的结果集大小,byte数
nscanned:扫描记录数量
nquery:后面是查询条件
nreturned:返回记录数及用时
millis:所花时间
如果发现时间比较长,那么就需要作优化。
比如nscanned数很大,或者接近记录总数,那么可能没有用到索引查询。
reslen很大,有可能返回没必要的字段。
nreturned很大,那么有可能查询的时候没有加限制。
mongo可以通过db.serverStatus()查看mongod的运行状态
db.stats()查看某一个库的原先状况
查看集合记录用
mongostat命令查看运行中的实时统计,表示每秒实时执行的次数
mongodb还提供了一个机遇http的监控页面,可以访问http://ip:28017来查看,这个页面基本上是对上面的这些命令做了一下综合,所以这里不细述了。
根据上面这些监控手段,找到问题后,我们可以进行优化
上面找到了某一下慢的命令,现在我们可以通过执行计划跟踪一下,如
可以通过 db.collection.ensureIndex({"字段名":1}) 来创建索引,1为升序,-1为降序,在已经有多数据的情况下,可用后台来执行,语句db.collection.ensureIndex({"字段名":1} , {backgroud:true})
获取索引用db.collection.getIndexes() 查看
这里我们创建一个user.uid的索引 >db.order.ensureIndex({"user.uid":1})
创建后重新执行
扫描数量减少,速度提高。mongodb的索引设计类似与关系数据库,按索引查找加快书读,但是多了会对写有压力,所以这里就不再叙述了。
2.其他优化可以用hint强制索引查找,返回只是需要的数据,对数据分页等。
查看日志看到大量如下日志:
Getting connection refused because too many open connections: 819
使用db.serverStatus().connections查看连接数:
> db.serverStatus().connections
{ "current" : 71, "available" : 748 }
current是当前占用的连接数, available表示空闲的
这个错误产生的原因主要是因为linux每个进程1024个文件句柄限制导致的。使用ulimit -n 或 ulimit -a 查看当前系统限制。
使用ulmit -n 65535 只会更改当前session的文件句柄限制,要更改系统的限制需vim /etc/security/limits.conf 添加如下行:
* soft nofile 65535
* hard nofile 65535
mongo的配置文件也需要修改 vim /etc/mongodb.conf
maxConns=20000 #mongo连接数不能超过20000
ps. linux系统做服务器一般都需要做些优化,这里给出一些常用优化项,vim /etc/sysctl.conf:
net.ipv4.tcp_max_tw_buckets = 819200
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_max_syn_backlog = 819200
#net.ipv4.tcp_syncookies = 1
net.core.somaxconn = 262144
net.core.netdev_max_backlog = 262144
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_fin_timeout = 1
net.ipv4.tcp_keepalive_time = 30
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.tcp_syncookies = 0
net.ipv6.conf.all.disable_ipv6 = 1
vm.swappiness=5
为了解决连接数过大的问题也算是翻遍了pymongo代码,发现两个选项,能起点作用
auto_start_request: 设为True时,MongoClient为每个线程分配一个socket, 这样比较安全,它能保证你在一个unacknowledged 写之后的读也是对的。而设为False时,多个线程共用socket, 不过如果你还想在单个socket上做序列操作以保证read-your-own-writes consistency, 可以使用 start_request/end_request. 更多详情请看:http://emptysqua.re/blog/requests-in-python-and-mongodb/
use_greenlets: 为True时,使用gevent时,该参数设为True, start_request会保证当前greenlet会使用同一个socket
mongostat是个非常好用的监控工具
$mongostat
insert query update delete getmore command flushes mapped vsize res faults locked % idx miss % qr|qw ar|aw netIn netOut conn time
2 6 3 0 0 6 0 4.44g 13g 772m 0 0 0 0|0 0|0 3k 40k 71 16:12:48
10 13 13 1 0 18 0 4.44g 13g 775m 0 0.3 0 0|0 0|0 46k 29k 71 16:12:49
6 7 7 0 0 14 0 4.44g 13g 772m 0 0.2 0 0|0 0|0 7k 5k 71 16:12:50
0 0 0 0 0 1 0 4.44g 13g 772m 0 0 0 0|0 0|0 62b 1k 71 16:12:51
2 3 3 0 0 6 0 4.44g 13g 772m 0 0 0 0|0 0|0 3k 4k 71 16:12:52
47 32 54 2 0 66 0 4.44g 13g 776m 0 1.1 0 0|0 0|0 422k 160k 71 16:12:53
0 0 0 0 0 1 0 4.44g 13g 776m 0 0 0 0|0 0|0 62b 1k 71 16:12:54
8 10 10 0 0 19 0 4.44g 13g 776m 0 0.1 0 0|0 0|0 11k 8k 71 16:12:55
4 5 5 0 0 10 0 4.44g 13g 776m 0 0 0 0|0 0|0 5k 5k 71 16:12:56
0 0 0 0 0 1 0 4.44g 13g 776m 0 0 0 0|0 0|0 62b 1k 71 16:12:57
insert/query/update/delete: 每秒 插入/查询/更新/删除数
getmore: 查询时游标的getmore操作数
command: 每秒执行命令数
flush: 一秒内flush的次数,flush开销很大,如果频繁flush, 可能需要查查原因了
mapped: 所有被mmap的数据量,单位是MB
vsize: 虚拟内存使用量
res: 物理内存使用量
faults: 每秒访问失败数(只有Linux有),数据被交换出物理内存,放到swap。不要超过100,否则就是机器内存太小,造成频繁swap写入。此时要升级内存或者扩展
locked: 写锁所占时间比,该数值过大(>10%)就需要注意了
idx miss: 索引不命中百分比,正常情况下,所有查询都应该通过索引
qr|qw ar|aw: mongo负载高时,命令来不及处理,mongo将命令放入队列。 qr|qw 等待读/写的队列长度 ar|aw 执行读/写操作客户端数量
netIn/netOut: 出入网络带宽
conn: 当前连接数
查看/创建/删除 索引
db.collection.getIndexes();
db.collection.ensureIndex({x: 1})
db.collection.dropIndexes();
db.collection.dropIndex({x: 1})
在后台创建索引(线上数据库加索引为了不影响正常应用)
db.collection.ensureIndex({x: 1, y: 1}, {background: true});
重建索引(collection大小变动巨大或索引占用过多空间时执行)
db.collection.reIndex();
查看数据库信息
db.stats();
服务器状态
db.serverStatus();
mongodb 当前正在执行的操作
db.currentOp()
如果你发现一个操作太长,把数据库卡死的话,可以用这个命令杀死他
db.killOp("xxxx")
mongo是出了名的吃内存,空间碎片问题严重,目前也没有什么好办法。
可以用db.repaireDatabase() 整理数据库,非常慢,线上系统就不要用了。
查看系统内存
free -m
total used free shared buffers cached
Mem: 15793 6879 8913 0 385 4102
-/+ buffers/cache: 2391 13401
Swap: 8183 0 8183
linux可用内存计算方法是free + buffers + cached, 实际使用内存是 used - buffers - cached
MongoDB使用的是内存映射存储引擎,它会把数据文件映射到内存中,如果是读操作,内存中的数据起到缓存的作用,如果是写操作,内存还可以把随机的写操作转换成顺序的写操作,总之可以大幅度提升性能。MongoDB并不干涉内存管理工作,而是把这些工作留给操作系统的虚拟内存管理器去处理,这样做的好处是简化了MongoDB的工作,但坏处是你没有方法很方便的控制MongoDB占多大内存,幸运的是虚拟内存管理器的存在让我们多数时候并不需要关心这个问题。