分类 日常 下的文章

mongostat的功能类似于Unix / Linux文件系统使用vmstat,监控数据库上各项操作的统计,如增删查改的数量、联机数、內存使用状况…等,默认情况下,mongotop返回值的每一秒。

官方详细文档:https://docs.mongodb.com/manual/reference/program/mongod/#bin.mongod

1. mongostat 使用帮助及参数说明

image.png

2. 监控数据

image.png

参数说明

  • insert 表示每秒插入数据库的对象数量,如果跟在一个*后面,表示这是复制操作、
  • query 每秒查询操作数量、
  • update 每秒更新操作数量、
  • delete 每秒删除操作数量、
  • getmore 每秒get more操作的数量、
  • command 每秒执行数据库命令操作的数量(比如插入、查找、更新、删除等等)、
  • flushes 每秒执行fsync操作的数量、
  • mapped 映射数据的总量,以兆字节M表示。这里的数据是从上次mongostat显示到这次的数量、
  • vsize mongod或mongos进程用掉的虚拟内存,以兆字节M表示、
  • locked db 这里的值表示当前列出的数据库在锁定状态上花销的时间加上mongod进程在全局锁上花销的时间,以百分比表示、
  • idx miss 表示需要一个页面错误来加载一个Btree节点的索引访问尝试的百分比、
  • qr 客户端等待从MongoDB实例读操作的队列长度、
  • qw 客户端等待从MongoDB实例写操作的队列长度、
  • ar 正在执行读操作的客户端数量、
  • aw 正在执行写操作的客户端数量、
  • netIn MongoDB实例接收到的网络流量,用字节bytes表示,包括mongostat本身连接MongoDB实例产生的流量、
  • netOut MongoDB实例发送出去的网络流量,用字节bytes表示,包括mongostat本身连接MongoDB实例产生的流量、
  • conn 打开的连接数总数、
  • set replica set的名称、
  • repl replica set的状态 PRI 表示是Primary,SEC表示是Secondary

问题: 哪些工具可以实时监控MongoDB的整体性能?
答案是:我觉得最合适是mongostat ,虽然mongotop 可以看到具体集合,综合性能还是mongostat合适。

一般来说,我们想看目前机器的负载,可以使用mongostat,这个数值的多少,没有一个标准答案,每个人的服务器配置不同,业务代码sql、表的数据量都会影响着这个数值。

mongodb:监控MongoDB实例上有写读操作的集合,在读写上的时间;每个数据库上读写入锁的时间,默认情况下,mongotop返回值的每一秒。

官方详细文档:https://docs.mongodb.com/manual/reference/program/mongotop/

mongodb使用帮助及参数说明

image.png

监控数据、数据每秒刷新

image.png

  • ns:包含数据库命名空间,后者结合了数据库名称和集合。
  • db:包含数据库的名称。名为 . 的数据库针对全局锁定,而非特定数据库。
  • total:mongod在这个命令空间上花费的总时间。
  • read:在这个命令空间上mongod执行读操作花费的时间。
  • write:在这个命名空间上mongod进行写操作花费的时间。
  • 时间: 当前状态的db时间
mongotop 使用在数据查询数据卡、慢、查询不出来的情况下使用。total 保持为0最好,有时冒出个100ms-200ms问题不大,一般大于500 可以考虑给当前表做索引优化,结合慢查询日志找出mongod执行慢的原因。

多年运维Mongodb,以下经验,非常认可,分享给大家。

1.mongodb 表名和字段名统一用小写字母

mongodb 是默认区分大小写的,为了避免以前在 mysql 下遇到的大小写敏感导致程序访问频频出错,
建立规范,mongodb 的表名和字段名都用小写字母命名。

2.尽可能的缩短字段名的长度

mongodb 的 schema free 导致了每笔数据都要存储它的 key 以及属性,这导致了这些数据的大量冗余。
开发人员也许考虑到,从易读性出发设计的 key 名,基本都是按照字面意思去设计的,这导致 key 很长,对应的数据存储占用了很大的空间。
所以,在你的程序里维护一套字典即可,尽可能降低 key 的长度。
譬如:
static final String CONTENT = "content";
static final String CONTENT_TYPE = "ctype";
static final String CONTENT_LENGTH = "clen";

这个很重要,如果你单表有1000W条记录,如果字段名称,减少几个字母,你想想可以节约多少空间,查询速度提高多少。

3.记住,mongodb 的查询每次只能用到一个索引

对于较复杂的表结构,可能会导致你频频使用联合索引。
但记住:
1)mongodb 单表最大索引数为 64 。
2)索引越多,插入或修改记录就会导致 mongodb 越慢。写锁会阻塞读请求,写得越慢,阻塞读请求越多、阻塞时间越长。
所以,索引越加越多的时候,你可能需要审视一下表结构设计的合理性。

这个很容易理解,索引是在这个表里,内存保留的另外一份数据,当索引多的时候,插入数据需要生成多份,所以就慢了。

4.客户端连接数大小的设置

mongodb-java-driver 的连接池,目前从观察到的情况是应用一开启便根据 connectionsPerHost 变量的设置,建立全部连接,然后提供给程序使用,并且一旦其中某个连接到数据库的访问失败,则会清空整个连接池到这台数据库的连接,并重新建立连接。
而 mongodb 对中断连接的垃圾清理工作则是懒惰的被动清理方式,如果驱动程序端配置的连接数过大,一旦发生重连,则会导致 mongo 服务器端堆积大量的垃圾连接以及对应数据,导致主机资源耗尽。
建议: mongodb 驱动的连接池大小的设置一般应该控制 100 左右。
( 参考阅读:PHP-FPM模式下可怕的 MongoDB-PHP-Driver 连接池无节制连接问题)

5.实例分离

mongodb 对数据库的访问全部加锁。
如是查询请求则设置共享锁。
数据修改请求则设置全局排他锁,且是实例级别的排他锁。
写锁会阻塞读请求,如果长时间持有写锁,会阻塞整个实例的读请求。
建议:
1)不同应用不应该共用同一个实例,防止互相阻塞!
2)如服务器资源不足,共用同一个实例,要保证读写特性相同,如都是读多写少,防止一台写多应用阻塞读请求。
(评语:旧版本的MongoDB (pre 2.0)拥有一个全局的写入锁,这个问题在2.0版本中的得到了显著的改善,并且在当前2.2版本中得到了进一步的加强。MongoDB 2.2使用数据库级别的锁在这个问题上迈进了一大步。所以用 MongoDB 2.2的人可以忽略此条目。)

如果有条件,这个很重要,一个应用如果表设计的特别糟糕,会影响整个实例的效率,这个不只是2.2版本,最新的mongodb3.4也一样。

6.需要重点关注的 mongodb 性能指标

关注主要性能指标:
1)Faults:显示 mongodb 每秒页面故障的数量,这个是 mongodb 映射到虚拟地址空间,而不是物理内存。这个值如果飙高的话,可能意味着机器没有足够的内存来存储数据和索引。
2)Flushes:每秒做了多少次 fsync,显示多少次数据被刷新进了磁盘。
3)locked:写锁。
4)idx miss:索引未命中比例。
5)qr | qw:读写锁的请求队列长度。
6)conn: 当前已经建立的连接数。

这几个性能指标都很重要,当然,你从外表去观察,也就连接数了。

7.严重的空间碎片问题

mongodb 如果数据修改很频繁,会出现比较严重的空间碎片问题,表现在磁盘文件扩张与实际数据量不相符,内存不够用,索引命中率低,查询效率降低。
碎片整理,目前我们采用的版本没有太有效的方法。
可以用 db.repaireDatabase() 来整理数据库,这个过程非常的慢。
如果是 master/slave 模式,则相当于执行一次主从切换,然后从新建立从库。
如果是 replSet 架构,可以停掉数据库,然后删除数据目录,从新从复制组中全同步数据,这个时候要考虑 oplog 的尺寸。
一个大体的步骤:
1)先调用rs.freeze(1200),将每个不想让它成为 primary 的机器让它在 1200 秒内无法成为 primary(这步也可以不做);
2)将 primary stepDown,不出意外新的 primary 会起来;
3)将原 primary kill 掉;
4)删掉所有 data 数据(调用 repair 很慢,真不如干掉重新来);
5)再重启动原 primary 的进程;
6)以此循环完成整个复制组的全部重建。

这个多年的库会存在大量碎片,但速度实在太慢,如果大数据,这个操作可要费点功夫了

8.连接池 WriterConcern 模式选择

有些应用配置了 WriterConcern.FSYNC_SAFE 模式;这种配置意味着客户端在插入数据或更新数据的时候,要求 mongodb 必须将所更新的数据写入磁盘并返回更新成功的信息给程序。
如果碰上应用程序访问压力大,mongodb 就会反应迟钝,并可能会假死。
针对此情况,需要评估数据的一致性需求,做出合适调整。
我们一般建议关闭此选项。
(评语:刘奎波的业务中心优化时就关闭了这个 WriterConcern.FSYNC_SAFE 模式)

9.开发时注意的细节

1)更新某条数据的时候,先查出来再更新会减小锁的时间;
2)只有真正需要的字段才select出来;
3)只有返回很少结果的查询才用索引,否则会加载太多数据,比没有用索引还慢!
4)属性比较多的时候,建立分层的关系能够提高查询效率,否则每个记录都要过一遍才能找到要的属性。(评语:貌似说的是以 Array 形式存储的 subdocument)
5)skip+limit 翻页,越往后面越慢。比较靠谱的做法是,先找出上次的id,翻页的时候不用 skip:
last_row_id = ObjectId('....');
db.activity_stream->find({_id:{$lt: last_row_id },user_id:20 } ).sort( {_id:-1} ).limit(10);

这个第一条Bmob后端云等平台,只提供基于objectid更新数据,也就是这个道理,让大家先查询出来,再更新。 第二条,需要大家自己优化,平台没法给你操作。 第五条其实是最常用的优化手段,在性能一般的情况,50w数据,就需要这样操作了

10.关于硬件资源的选择

虚拟机可以很好的隔离资源,并可动态的扩展。
我们建议 mongodb 的部署采用虚拟机的方式,每个虚拟机部署一个实例,使各节点分散在不同的物理机上,根据应用的前期预测,平衡虚拟机的之间的i/o。

有开发者问Bmob能否用wepy,我的答案是可以的,也已经有数十位基于Bmob sdk开发者使用wepy做的小程序上线了

安装wepy
以下安装都通过npm安装,首先电脑安装npm

安装 wepy 命令行工具。
npm install wepy-cli -g
在开发目录生成开发DEMO。

wepy new myproject
切换至项目目录。

cd myproject

开发实时编译。
wepy build --watch

安装.png

这是安装过程

然后开发工具选择 dist 目录打开,默认情况下会报错,大家开启
image.png

记得这个操作,不然会报上面的错误,微信开发者工具 --> 项目 --> 关闭ES6转ES5。

然后就是建个文件引入Bmob了,跟之前引入Bmob并无差异。

├── dist                   微信开发者工具指定的目录(该目录由WePY的build指令自动编译生成,请不要直接修改该目录下的文件)
├── node_modules           
├── src                    代码编写的目录(该目录为使用WePY后的开发目录)
|   ├── components         WePY组件目录(组件不属于完整页面,仅供完整页面或其他组件引用)
|   |   ├── com_a.wpy      可复用的WePY组件a
|   |   └── com_b.wpy      可复用的WePY组件b
|   ├── pages              WePY页面目录(属于完整页面)
|   |   ├── index.wpy      index页面(经build后,会在dist目录下的pages目录生成index.js、index.json、index.wxml和index.wxss文件)
|   |   └── other.wpy      other页面(经build后,会在dist目录下的pages目录生成other.js、other.json、other.wxml和other.wxss文件)
|   └── app.wpy            小程序配置项(全局数据、样式、声明钩子等;经build后,会在dist目录下生成app.js、app.json和app.wxss文件)
└── package.json           项目的package配置

上面是项目目录结构说明,这里需要注意的是wepy引入js需要2份才不会报错,在目录src 下放一份BmobSDk , dist目录下面放一份。

小程序接下来就愉快的操作Bmob 云数据库了。