热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

接口机磁盘读写极度不均衡的原因分析背景正文数据写入磁盘的过程NIFI使用本地磁盘的特性PageFaults带出的缓存使用问题的最终定论背景在进行服务器接口机的资源梳理时ÿ

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


  • 背景
  • 正文
    • 数据写入磁盘的过程
    • NIFI使用本地磁盘的特性
    • PageFaults带出的缓存使用
    • 问题的最终定论



背景

在进行服务器接口机的资源梳理时,发现一个现象——接口服务器的磁盘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是持平的。






推荐阅读
  • Android与JUnit集成测试实践
    本文探讨了如何在Android项目中集成JUnit进行单元测试,并详细介绍了修改AndroidManifest.xml文件以支持测试的方法。 ... [详细]
  • 入门指南:使用FastRPC技术连接Qualcomm Hexagon DSP
    本文旨在为初学者提供关于如何使用FastRPC技术连接Qualcomm Hexagon DSP的基础知识。FastRPC技术允许开发者在本地客户端实现远程调用,从而简化Hexagon DSP的开发和调试过程。 ... [详细]
  • 从理想主义者的内心深处萌发的技术信仰,推动了云原生技术在全球范围内的快速发展。本文将带你深入了解阿里巴巴在开源领域的贡献与成就。 ... [详细]
  • 本文详细介绍了如何搭建一个高可用的MongoDB集群,包括环境准备、用户配置、目录创建、MongoDB安装、配置文件设置、集群组件部署等步骤。特别关注分片、读写分离及负载均衡的实现。 ... [详细]
  • 流处理中的计数挑战与解决方案
    本文探讨了在流处理中进行计数的各种技术和挑战,并基于作者在2016年圣何塞举行的Hadoop World大会上的演讲进行了深入分析。文章不仅介绍了传统批处理和Lambda架构的局限性,还详细探讨了流处理架构的优势及其在现代大数据应用中的重要作用。 ... [详细]
  • 本文探讨了在Windows系统中运行Apache服务器时频繁出现崩溃的问题,并提供了多种可能的解决方案和建议。错误日志显示多个子进程因达到最大请求限制而退出。 ... [详细]
  • 在尝试通过自定义端口部署Spring Cloud Eureka时遇到了连接失败的问题。本文详细描述了问题的现象,并提供了有效的解决方案,以帮助遇到类似情况的开发者。 ... [详细]
  • 实践指南:使用Express、Create React App与MongoDB搭建React开发环境
    本文详细介绍了如何利用Express、Create React App和MongoDB构建一个高效的React应用开发环境,旨在为开发者提供一套完整的解决方案,包括环境搭建、数据模拟及前后端交互。 ... [详细]
  • Flutter 核心技术与混合开发模式深入解析
    本文深入探讨了 Flutter 的核心技术,特别是其混合开发模式,包括统一管理模式和三端分离模式,以及混合栈原理。通过对比不同模式的优缺点,帮助开发者选择最适合项目的混合开发策略。 ... [详细]
  • Kafka入门指南
    本文将详细介绍如何在CentOS 7上安装和配置Kafka,包括必要的环境准备、JDK和Zookeeper的配置步骤。 ... [详细]
  • 腾讯云移动推送TPNS(Tencent Push Notification Service)为APP开发者和运营人员提供了一站式、高效、稳定的推送解决方案,帮助提升用户活跃度和运营效率。 ... [详细]
  • 深入解析Dubbo:使用与源码分析
    本文详细介绍了Dubbo的使用方法和源码分析,涵盖其架构设计、核心特性和调用流程。 ... [详细]
  • SDWebImage第三方库学习
    1、基本使用方法异步下载并缓存-(void)sd_setImageWithURL:(nullableNSURL*)urlNS_REFINED_FOR_SWIFT;使用占位图片& ... [详细]
  • Spring Boot与Graylog集成实现微服务日志聚合与分析
    本文介绍了如何在Graylog中配置输入源,并详细说明了Spring Boot项目中集成Graylog的日志聚合和分析方法,包括logback.xml的多环境配置。 ... [详细]
  • 本文介绍了在 CentOS 7 系统中如何查看所有活动进程及其运行时间。通过使用 `netstat` 和 `ps` 命令,您可以轻松获取进程的详细信息,包括启动时间、用户、终端和命令等。 ... [详细]
author-avatar
Kermit68_629
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有