【Linux】接口机磁盘读写极度不均衡的原因分析

文章分析了一台接口服务器磁盘写入远大于读取的现象,发现NIFI进程大量占用写入资源,利用Linux的文件系统缓存机制解释了这一现象。NIFI在内容存储时使用本地磁盘,数据先写入缓存,减少了读取操作。PageFaults的minflt指标显示数据主要从缓存中获取,而非频繁读取磁盘。手动干预缓存使用,如使用sync命令,可使读写流量趋于平衡。
摘要由CSDN通过智能技术生成

背景

在进行服务器接口机的资源梳理时,发现一个现象——接口服务器的磁盘IO中,数据写入的流量远大于读取流量,实时的监控图表如下:
在这里插入图片描述

实时在服务器使用iotop命令查看读写情况,也能看到差异是非常大的:

Total DISK READ :      75.31 K/s | Total DISK WRITE :      77.38 M/s
Actual DISK READ:      96.82 K/s | Actual DISK WRITE:    1117.56 M/s
   PID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND                                                                                 
 19969 be/3 root        0.00 B/s 1061.48 K/s  0.00 % 78.56 % [jbd2/sdb1-8]
104428 be/4 root       39.45 K/s    0.00 B/s  0.00 % 75.63 % [kworker/u449:0]
158908 be/4 user   35.86 K/s    0.00 B/s  0.00 % 64.11 % sshd: user@internal-sftp
   350 be/4 root        0.00 B/s   25.73 M/s  0.00 % 57.42 % [kswapd0]
178091 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.03 % [kworker/35:2]
226695 be/4 user     0.00 B/s    3.59 K/s  0.00 %  0.01 % java -Dzookeeper.log.dir=/data1/app/zookeeper~/zookeeper/zookeeper-3.5.9/bin/../conf/zoo.cfg
 18862 be/4 user     0.00 B/s   50.55 M/s  0.00 %  0.01 % java -classpath /data1/app/nifi/nifi-1.16.3/.~app/nifi/nifi-1.16.3/logs org.apache.nifi.NiFi
 19999 be/3 root        0.00 B/s   39.45 K/s  0.00 %  0.00 % auditd
138967 be/4 root        0.00 B/s    3.59 K/s  0.00 %  0.00 % rsyslogd -n

基于此问题,还是需要进行一下分析,理解一下为什么会出现这个现象。

正文

数据写入磁盘的过程

针对这个现象,第一个切入点其实不是进程的io占用,因为监控和iotop的表现基本一致,说明实际是有这么高的数据写入需求;

然后另一个需要注意的特征点是,在这样巨大差异的场景下,业务数据也是正常传输的,因此,首先要去想清楚,写入的数据和读取得数据是怎么发生的;

根据iotop的信息显示,发现pid为18862的进程会长期占用大量的Write资源:

[root@hostname ~]# ps -ef|grep 18862
user   18862  18785 99  2022 ?        660-04:39:50 /usr/jdk64/jdk1.8.0_112/bin/java -classpath /data1/app/nifi/nifi-1.16.3/./conf:/data1/app/nifi/nifi-1.16.3/./lib/logback-core-1.2.11.jar:/data1/app/nifi/nifi-1.16.3/./lib/nifi-properties-1.16.3.jar:/data1/app/nifi/nifi-1.16.3/./lib/logback-classic-1.2.11.jar:/data1/app/nifi/nifi-1.16.3/./lib/jetty-schemas-5.2.jar:/data1/app/nifi/nifi-1.16.3/./lib/log4j-over-slf4j-1.7.36.jar:/data1/app/nifi/nifi-1.16.3/./lib/nifi-framework-api-1.16.3.jar:/data1/app/nifi/nifi-1.16.3/./lib/nifi-nar-utils-1.16.3.jar:/data1/app/nifi/nifi-1.16.3/./lib/nifi-runtime-1.16.3.jar:/data1/app/nifi/nifi-1.16.3/./lib/jcl-over-slf4j-1.7.36.jar:/data1/app/nifi/nifi-1.16.3/./lib/slf4j-api-1.7.36.jar:/data1/app/nifi/nifi-1.16.3/./lib/nifi-property-utils-1.16.3.jar:/data1/app/nifi/nifi-1.16.3/./lib/javax.servlet-api-3.1.0.jar:/data1/app/nifi/nifi-1.16.3/./lib/jul-to-slf4j-1.7.36.jar:/data1/app/nifi/nifi-1.16.3/./lib/nifi-server-api-1.16.3.jar:/data1/app/nifi/nifi-1.16.3/./lib/nifi-api-1.16.3.jar:/data1/app/nifi/nifi-1.16.3/./lib/nifi-stateless-bootstrap-1.16.3.jar:/data1/app/nifi/nifi-1.16.3/./lib/nifi-stateless-api-1.16.3.jar -Dorg.apache.jasper.compiler.disablejsr199=true -Xmx48g -Xms48g -Dcurator-log-only-first-connection-issue-as-error-level=true -Djavax.security.auth.useSubjectCredsOnly=true -Djava.security.egd=file:/dev/urandom -Dzookeeper.admin.enableServer=false -Dsun.net.http.allowRestrictedHeaders=true -Djava.net.preferIPv4Stack=true -Djava.awt.headless=true -XX:+UseG1GC -Djava.protocol.handler.pkgs=com.dtsw -Dnifi.properties.file.path=/data1/app/nifi/nifi-1.16.3/./conf/nifi.properties -Dnifi.bootstrap.listen.port=22995 -Dapp=NiFi -Dorg.apache.nifi.bootstrap.config.log.dir=/data1/app/nifi/nifi-1.16.3/logs org.apache.nifi.NiFi

根据ps查询的结果,这是一个nifi进程, 那么它应该就是产生数据流向的主要进程了;

接下来从这个进程出发,我们继续探寻IO差异的原因;

在这里,我们首先要知道Linux进行数据读写时的流程,这将有助于我们进一步定位这个问题,首先,让我们思考一个问题:

当我们在Linux写入一个文件的时候,文件是直接写入磁盘的吗?

既然我问出来这个问题,那么你一定会回答“不是”,既然不是的话,这个流程应该是怎样的?

实际上,对于操作系统的操作而言,提高文件写入性能的一个简单方法是让操作系统缓存数据,这个时候会告诉应用程序文件是写入了的,然后再异步的执行写入操作;

这样如果同时有其他磁盘活动将会非常高效,操作系统可以优先读取并稍后执行写入操作。并且在极端情况下还可以完全消除实际写入的需要,例如,在临时文件很快被删除的情况下。

当然,像这样的缓存也有缺点,我们可以想象到,极端情况下,我还没有在终端确认保存操作,那么有些数据可能在真正保存之前就丢失了。

如果编辑器告诉用户写入成功,但文件实际上不在磁盘上,在业务逻辑上一定是不正确的。这就是为什么会有fsync()之类的系统调用,在向用户报告写入成功之前,文件操作程序可以使用它来确保数据正常。

NIFI使用本地磁盘的特性

ok,了解了文件的读写大致流程,我们现在可以顺理成章的给出一个能够和nifi特性匹配的推测:

在配置了本地磁盘作为Content Repository之后,当nifi从A服务器拉取一个数据的时候,在本地会落盘一次,于是数据会写入缓存,下游的nifi推送组件(或者别的需要使用这个文件的流程)可以直接从缓存中拿到这个文件,而不需要产生新的IO操作;

这里的大前提就是nifi的nifi.content.repository.implementation配置项使用的默认值org.apache.nifi.controller.repository.FileSystemRepository

# Content Repository
nifi.content.repository.implementation=org.apache.nifi.controller.repository.FileSystemRepository
nifi.content.claim.max.appendable.size=50 MB
 
nifi.content.repository.directory.content1=/data1/app/nifi/nifi_repository/content_repository
nifi.content.repository.directory.content2=/data2/app/nifi/nifi_repository/content_repository

个别的环境可能这个值配置成了VolatileContentRepository,按照现在的推测,这种配置下不会出现write远大于read的情况,因为write本身也不会落盘,所有操作都在内存中进行;当然,如果出发了极端操作,也可能会产生上述现象,具体的需要进一步测试,暂且不提;

PageFaults带出的缓存使用

现在我们有了一个大概的推测,接下来我使用pidstat命令针对nifi的进程进行进一步的排查:

在这里插入图片描述
这里注意几个值,一个是minflt/s还有一个是kB_rd/s

后者可以很好的理解,是从磁盘读的数据量,单位kB,和监控的情况保持一致,基本没有读,只有写(kB_wr/s);

那么minflt这个值为什么要关注呢?这个指标的含义是什么?

minfltminor page faults,通过man文档我们可以查询到这个指标的解释:

Total number of minor faults the task has made per second, those which have not required loading a memory page from disk.

直译过来就是每秒发生的次要错误总数,这些错误不需要从磁盘加载内存页;这里的错误指的就是页面错误,想要理解这个指标的含义,我们就要多了解一下页面错误是什么。

我们知道,操作系统使用分页在主存储器和辅助存储器之间传输数据(主存储器就是RAM内存,CPU可以直接访问,辅助存储器通俗来说就是我们常说的磁盘)以实现高效的内存管理;

页(内存/虚拟页)是正在运行的进程的一部分,表示一个逻辑内存单元;内存中包含单个进程页的物理部分称为帧;所有帧都是固定长度,允许非连续(非共享)分配物理内存地址空间;

而我们所说的页错误(page fault)是由内存管理单元引发的异常,当进程需要访问其地址空间内的数据时,它无法加载物理内存;

异常通常指示计算机在虚拟内存中找到该数据块,这样它就可以从存储设备发送到物理内存;page fault其实很常见,通过提高程序的内存分配,通常有助于提高性能;

minor page faults,也称为软错误,发生在内存页由多个程序共享时,其中一些程序已经将该页带到主内存中;也就是说,在访问一个地址时,与之绑定的虚拟内存空间对应的地址空间已经被内核加载到了Page Cache中,那么此时只需要把该Page映射到vma中即可;

现在,让我们回到pidstat的跟踪排查结果上来,minflt发生的非常频繁,说明需要操作的数据总是能从缓存中找到,所以,实际上并不会产生大量的磁盘io读操作。

问题的最终定论

现在,我们需要使用一些小手段,手动的介入缓存的使用,尽可能的在数据读入缓存后清理除去,我们使用sync命令调用响应的页回刷的函数,同时观察到磁盘的读操作一下变多了:
在这里插入图片描述
这样看可能并不直观,让我们结合已有的监控进行观察:

在这里插入图片描述
尽管只有很短的时间,但是我们可以看到磁盘的读写流量基本是持平了;

这就是因为我们手动介入,尽可能的在nifi将数据拉取过来时,从内存中把对应的数据刷写磁盘并清空缓存,导致下游的读取需要再走一次磁盘;由此产生了大量的read流量,并且,随着程序运行,缓存会逐渐增多,所以后续read流量越来越少,逐渐变成之前的状态了。

极端状态下,如果没有缓存机制,真正意义上read和write是持平的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Meepoljd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值