【真实案例】程序设计的陷阱-警惕大块数据
无论是架构还是程序的设计,往大的方面说,逃不过分治、分类的范畴。微服务是一种分治的思想;service mesh是一种分治的思想;G1是一种分治的思想;大数据是一种分类的思想;领域划分是一种分类的思想……
数据大了会遇到问题,比如经典的redis的大key问题。遇到这类问题,主流解决方案是两个。
一个是限制,比如超过多大就不处理了,只提示错误。
另外一个是分治,将数据分片处理。
如果这两个都没做?
案例一:请求超时了
问题
首先回顾《近期做的稳定性建设总结》里的一个案例:收到一个可用内存不足10%的告警,经过了两个小时才自动恢复到87%,所在的机器出现一笔请求超时。
现象
1、对业务进行了解发现开始出现问题的时间点,有收到外部MQ发过来的大块数据。虽然大块数据做了分片,但是每个分片还是有几M大并且下发条数多,集中下发。
2、分别在问题机器和其他相同应用的不同机器上执行top命令,观察到问题机器和其他机器上jvm内存占用差不多并且稳定。
3、执行free -h命令观察到机器可用内存7.6G,cache/buffer下降到1G以下。
4、收到外部MQ发过来的大块数据时间段网卡占用情况从平时0.5M以下飙升到20M/S。
5、出现一笔请求超时正在下发大块数据的高峰期,请求方发请求到机器接收到请求时间间隔是几毫秒。收到请求后机器会将请求转发给MQ,用了3秒多。而请求方设置了5秒超时。
6、所有环节cpu使用率都很低。
7、20天前在另外一台机器上也发生了内存升高到90%以上的现象。
总结来说:MQ集中接收10M内的大块数据,JVM内存占用正常,操作系统内存占用升高,网卡流量20M/S。
分析
此问题的根本原因是外部短时间内下发大量大块数据(受节假日影响当天的大块数据略高于其他天),从根本上治理需要和外部(大佬级别,我们完全没有主动权)一起进行架构方面的调整,不现实也没有必要用此牛刀。
表现为虚机层面内存资源不足,影响了IO速率。因为高峰IO 20M/S,针对现在的千兆网卡、万兆网卡来说,并不大。问题并不在网络通道上,而在机器本身。
我写网络通信和linux操作系统系列也有一段时间了,大家可以用这个实际问题来小试牛刀。
在《深入浅出操作系统的零拷贝》中,我发起了一个投票:使用零拷贝最少要进行几次拷贝?
36%的人答对了,是2次。
零拷贝如果只是从磁盘发送到网络,最低可以只用2次DMA拷贝,也就是内核空间拷贝。但是实际情况是大多数场景,数据都需要回到用户空间进行数据处理,所以都需要3次以上。加上数据往往进来还要出去。所以10M的大块数据处理过程占用的内存是数倍于10M的。
所以最终我们提申请进行了纵向扩容。纵向扩容说白了就是增加物理内存。有人就要问了是不是扩机器数量也能解决问题。这种处理方式大概是能治病但是不一定对症。意思可以理解为吃广谱抗菌药来治疗感冒,有一定帮助但不是针对性的处理。因为扩机器是减少了请求数从而减少了内存占用量。但是万一有个大请求就是把内存飚高了呢?并且扩内存实施成本更低,不需要程序的发布。就是低峰期暂停程序,插个内存条。
经过半年多的验证,超时再没有发生过,说明我们的处理方式是对症的。
案例二:内存达到95%
问题
生产环境有个不太核心的服务,共三台机器,一台机器内存达到95%。这个服务分成定时任务和实时请求处理两部分,其实定时任务流量占比居多,实时请求很少。所以我们将这台机器隔离掉定时任务。也就是说定时任务只走另外两台。目前内存稳定在95%,但是内存达到这么高的原因需要找到。
服务的定时任务部分是调用RPC接口获取数据,之后RPC或者http请求发送到别处。每次处理的数据大小在几K。实时请求部分有个是处理文件上传的,文件的大小不固定,几M,几十M都是有可能的。这种请求一般情况下1天十笔,但是不固定。
现象
1、使用pmap -x java程序进程号 命令,发现大量anon块,举例如下:
b7f0a000 8 rwx-- 00000000b7f0a000 000:00000 [ anon ]
b7f20000 8 rwx-- 00000000b7f20000 000:00000 [ anon ]
2、这个服务除了内存使用达到95%的这台机器,另外一台达到了93%,还有一台89%。备用机房的机器(无定时任务,也不接受实时请求)内存占用基本都是86%。
分析
用pmap -x 查到的anon,中文叫匿名块。是通过malloc或mmap分配的“大”块。Java直接申请的堆内存之外的块就是堆外内存了。我查了代码,这个服务不涉及到数据库操作,没有使用guava等直接内存的。最有可能产生堆外内存的地方是nio(使用了netty、jetty)。
这个问题本来不是我在查,另外两个同事在做。但是一直没有结论,我就进行了介入。同事一直的思路是要找到内存泄露的地方。我分析可能不是内存泄露,因为最近几个月内存没有再增长。
我任务运行的这半年内,有段时间可能有人批量上传大文件。文件上传到哪台服务器是随机的,占用内存高的机器很有可能是当时处理的文件大或者多。因为过去了快半年了,日志都清理了,我没有办法找到直接的证据。
同事还是想排查内存泄露问题。给出两个理由:第一,把定时任务停掉的机器最近内存稳定不再增长了。第二,他查了最近的日志,文件大小都在几M,而内存高的机器要内存使用高出了几百M。
我的答复:内存不再增长与定时任务停掉未必是因果。也可能是最近再也没有如此大块或大量的文件上传。另外,程序没有做限制,最近只有几M未必几个月前没有更的文件上传。另外,案例一里也说了,如果文件大于十几兆,其使用的内存要多出好几倍。
同事觉得有道理,决定下周进行测试环境模拟验证,结果究竟如何呢?咱们来猜一猜。
总结
如果你的服务要处理大数据,但是又不想使用分治引入程序的复杂性,那么在申请机器的时候,要增加足够的内存冗余!
有的朋友会觉得这个处理方式并不高大上。牛逼的处理方式是排查到内核层,调整个linux参数啥的。不推荐!原因如下:
现在也算比较流行的一个词叫做:不可变服务器。“传统”的部署方式中,对系统的改动都会呈现在服务器上,从而增加了风险。采用不可变部署方式,服务器都是公司统一标准化的,那么变更只要考虑应用程序的发布,而应用程序一旦发布也不允许改变,就成了不可变服务。不可变性可以增加系统的稳定性。在一个规模比较大的公司,就算可以通过技术解决资源的问题,从整体角度,还是通用方案更加可取。
另外补充一个小知识:
使用pmap -x 命令查看内存地址的时候要注意起始地址:
64位的linux系统,对于虚拟地址空间的划分,将0x0000,0000,0000,0000 – 0x0000,7fff,ffff,f000这128T地址用于用户空间;而0xffff,8000,0000,0000以上的128T为系统空间地址。
文章转载自公众号:编程一生