最近一直在查一个mongodb的问题,大致现象是怎么样呢?
每天mongodb集群的其中一个shard的master节点,会不定时地出现load突然飙升的情况,32核的主机load飙到将近30又快速回落。
通过看监控图发现其TCP连接很不正常,会在一直保持两三百的连接数的情况下,在几秒钟时间一下子新建一千多的连接,然后在一分钟后释放这些新建的连接。这种情况在很频繁地发生,但是一天内只有几次是有load飙升的情况。再看磁盘IO,居然高峰的时候会有超过5秒的写IO等待,看图形就跟医院里的那个心跳检测仪一样,我的内心是崩溃的。
好吧,现象说到这里,先简单将一下mongodb集群,说得通俗一点——
mongodb集群里面有三种角色进程,mongos,mongodb-config库,mongodb-业务数据库;
其中mongos主要是用来做路由分发和集群配置管理的,而配置管理的数据就是存在config库中;
后两者都是起的mongodb的进程,只不过在集群中存储的数据类型不同;
mongos可以有多个,可以随时添加,在程序上可以把它当作连接器使用;
mongodb进程则可以分为多个组,每个组有多个镜像副本集,用于存储不同的数据;
mongodb的每个组我们叫它shard,副本集我们叫它replicate,每个shard相当于一个存储桶,你可以按照不同维度(比如按库名,按照集合名,甚至按照集合的某个字段)将数据分存到不同的桶里,以实现IO负载;
多个replicate一方面可以避免数据单点故障,一方面可以开启读写分离等机制分摊IO和连接压力。
一口气把自己五年mongo维护下来的体会说了一遍,初学者最好还是去官方文档看一看,最好看英文,因为我感觉这么多年下来,开源技术圈的中文翻译丝毫没有进步。
再讲一下我们的服务器配置,32核256G内存,500G云主机的高IO硬盘(实际数据只有不到300G),里面存了10个业务库的样子。
看到这个现象,我第一时间是让架构师(博客里叫他老苍吧)去查java连接器,TCP的连接新建和释放那么快,肯定是连接池配置有问题。为了让老苍重视,我还特地把他叫到我电脑前,对着监控图以及mongodb和java服务器的netstat状态记录给他看,说完撂下一句话“我去看看”就走了。
好家伙,过了一个礼拜,石沉大海,问题更严重了。
好吧,既然这样我还是找技术总监吧,整个研发团队里能够心有灵犀的也就那么几个人吧,一只手的指头可以掰得过来。
问题讲完一遍,他很认可我的质疑点,叫了一群肩上带花的(组长以上级别)开发,把目前java的mongo连接器的配置状态问了一遍,没人答话,老苍说了一句”mongo没有配置连接数上限或者超时之类的“。
”你确定?“
”我再去研究一下“
一去过了两天,说是找到答案——mongo的连接器默认的最小连接数配了1,最大连接数是100,但是没有设置连接超时。
好吧,那么TCP连接突然飙高的现象就可以解释了,那么一分钟后回落又是怎么回事呢?
还是要继续研究。。。
再说磁盘IO,我发现磁盘IO的问题也比较晚,因为在我的运维经验里,Mongo的读写IO都一般都不会很高(除非是机器刚重启完,或者刚从slave切成master)。
先来说写IO——Mongo的数据库存储不像mysql那样把单个表存储为一个文件,mongo里的表我们称之为”集合“,它的存储方式是首先存一个64MB的数据文件,存满了以后再开一个128MB的文件继续存,再存满的话再开一个256MB的文件,以此类推,最后集合数据越多其数据文件也就越多,为了防止单个文件无限制增长限定了最大是2G。而其写入的方式也是像写日志一样,顺序地往文档最后添加,而不会在之前的数据文件中间插一条进去。按照这样的原理,照理磁盘的IO吞吐量可以承载很大量的数据。
再说读IO——Mongo的进程会把每次查询的集合数据缓存在自己的内存中,所以需要服务器要配那么多内存,通常的查询(特别是重复对一个集合的查询)实际上是在读内存,而不是读磁盘。所以基本上磁盘很难会有读的压力,况且通常磁盘的读取IO要比写入IO高很多。
那么为什么会有IO写延时高的问题呢,我首先想到的是用mongostat和mongotop去记录一天的记录,前者可以看出进程运行时的各项性能,后者则会显示出当时的每个IO延时高的集合排名。这一下子定位到了几个频繁的业务集合。
开发组长“金星”这点还是蛮给力,一旦问题给他定位好了,给他讲解了一番原理和调整方案,好了该做缓存的做缓存,该砍掉的功能暂时砍掉,还找出了一个陈年老代码(这一个功能接口里面有十几个mongo查询和计算)。
好了,感觉可以歇一歇了,心情刚刚放松下来,第二天的监控load报警又来了!!!
我不得已,想想反正系统IO已经那么高了,也不差这么一点儿了,让DBA”小恶魔“把这个shard上所有库的全量事务日志都开了(我在这里就不介绍是怎么开的了,万一被小朋友学去把系统搞瘫了,还要怪我)。发挥我炉火纯青的shell三剑客功底,把load高峰时的所有集合操作做了统计,发现每次Mongo每次新建连接,都会进行一次sasl的认证,这个认证操作在并发量大(上百每秒)的时候非常慢(甚至会超过1秒),另外还发现居然有三个不同业务的集合有频繁update的操作(OMG)。
你要在一个大集合10几个数据文件(因为集合有超过10G,有些单个存储文件已经有2G了)里找到要修改的那几个数据文档,再对文件进行update,如果update的内容比原来的文档字节数多,还可能会引发数据文件重组的机制,想想都替那些硬盘觉得肉痛——感觉有点像你整理好的一个个储物箱,被老婆翻出来看了一遍,把游戏机拿了出来又放了一个化妆盒进去;恰好化妆盒把游戏机大,所以比这个箱子大的储物箱都要重新整理一遍。
这种底层的知识,我真的懒得跟开发和架构师一个个去科普,不是我脾气太差,只是我真的觉得很累,每个礼拜跟刑侦大队长一样在公司里”破案“。
隔了一天,架构师老苍根据自己对监控图的观察,和多年的工作经验,在群里@我了:我们mongodb的master节点的磁盘性能是不是比slave节点的要差。
——我平时是不是表现的太轻浮,让人觉得不靠谱了???
再问了一句,我们mongodb磁盘用的是ssd吧?
——这我在5年多三家公司用下来,还真没有给mongo服务器上过ssd硬盘,难道是我太抠了?以我的理解,mongo这种特性的数据库服务器,不应该这样去配吧,钱多也不是这么用的。不知道有没有其他公司的架构师有不同的看法,或者是在偷笑?
好吧,为了应付问题上ssd,你自己去跟总监说吧。我一个运维去说这个事情,我觉得丢人。
篇幅有点长了,写了一个小时了,未完待续。。。
来源:oschina
链接:https://my.oschina.net/u/4023559/blog/3126321