系统吃swap问题排查

目录

背景

问题

分析并解决

1.控制线程数

2.更换IO组件

3.Linux进程信息文件分析

总结加餐

参考文档

背景

隔壁业务组系统是简单的主从结构,写索引的服务(主)叫primary, 读索引并提供搜索功能的服务(从)叫replica。业务线同步数据并不是平滑的,在同步数据时primary瞬间负载上升,磁盘写IO增加,这是合理的。

primary 写完索引,会马上触发索引的同步操作,因此,瞬间的磁盘读IO增加以及网络流量的增加都是合理的。

问题

在节假日高峰期前期,团队增加了50%的replica服务器,这造成了primary的同步压力进一步增加,观测系统监控,发现了“吃swap”的现象,这就不合理了,需要分析优化。

这里简单解释下“吃swap”:

在计算机系统中,"吃swap"指的是系统开始使用交换空间(swap space)来补充物理内存(RAM)的不足。交换空间是一块预留在磁盘上的区域,用于存储那些当前不常用的内存数据。当物理内存不足时,操作系统会将一些数据从内存中移到交换空间,以腾出内存给其他需要的进程。

具体来说,“吃swap”通常表示以下几种情况:

  1. 内存不足:系统的物理内存已经被用完,导致操作系统不得不使用交换空间来存储数据。
  2. 性能下降:因为磁盘的读写速度远低于内存的读写速度,当系统频繁访问交换空间时,性能会显著下降。用户可能会注意到系统变得响应缓慢。
  3. 高负载场景:在高负载场景下,如大量的索引写入和同步操作,内存需求突然增加,导致系统不得不使用swap。

以上描述的情况下,primary服务器在索引写入和同步操作的高负载下,内存不足,导致操作系统开始频繁使用swap,从而出现“吃swap”的现象。这是不合理的,因为频繁使用swap会显著降低系统性能,影响整体服务的响应速度和稳定性。因此,需要通过优化措施来减少对swap的依赖,提高系统的性能。

分析并解决

1.控制线程数

参考以前的经验,replica 有个索引备份功能,在晚上4点会备份本地索引文件(30G)到指定目录
这个操作就会造成瞬间IO的增加以及负载的升高,因此最初我们判断这是索引复制太快,导致的内存耗尽和负载增加。我们尝试通过控制primary服务线程池的大小,来放慢索引复制的速度。

经过压测发现,线程很小(5个)的情况,载峰值有所降低,但是吃swap的问题仍然存在。

结论:primary采用的gRPC框架,通过控制线程池大小来放慢索引复制的速度在gRPC框架中并不奏效,因为gRPC的异步调用机制允许高并发执行,独立于线程数量。有效的优化策略需要针对异步调用特性和具体负载情况进行调整,而不仅仅是控制线程数。

2.更换IO组件

Lucene提供了两种磁盘访问方式MMapDirectory和NIODirectory,因为mmap方式会大量使用堆外内存,因此我们怀疑是堆外内存失控导致的系统内存不足,经过更换Directory,发现吃swap问题仍然存在。

Apache Lucene 是一个高性能、可伸缩的信息检索库,用于全文搜索和索引。Lucene 提供了多种方式来访问磁盘存储的索引数据,主要包括 MMapDirectoryNIODirectory

结论:mmap虽然会使用堆外内存,但是linux应该是可以控制好堆外内存的回收的。

3.Linux进程信息文件分析

我们分析了linux下的 /proc/$pid/status文件,VmSwap是swap占用情况,VmHWM是历史内存占用。以及java进程当前的内存占用情况 sudo -u tomcat jhsdb jmap --heap --pid $pid

发现java进程的历史内存占用很高,而当前内存占用并不高(所以排除了内存泄漏),各个进程都有占用swap的现象,说明是java进程在某个时刻(文件传输)耗尽了内存,导致其他进程吃swap,而那个时刻过后,java进程是可以回收内存的。所以推测是索引文件传输过程中,某些buffer设置不合理导致的。

经过代码排查 ,发现gRPG(高度异步)在读取索引文件时需要多次判断serverCallStreamObserver.isReady(),如果只判断一次isReady,就把所以数据写入响应流,就会占用大量系统缓冲区。官方推荐的是一种callback机制,示例代码如下:

final InputStream input = ...

serverCallStreamObserver.setOnReadyHandler(new Runnable() {

    int upto = 0// 传输偏移,需要反复使用

    final long size = file.length();

    public void run() { // 这里会根据缓冲区状态多次被触发执行,因此这个Runnable是有状态的

        while (upto < size && serverCallStreamObserver.isReady()) {

             response.onNext(...); //写入缓冲区

             upto += chunk;

        }

    }

});

serverCallStreamObserver.setOnCancelHandler(() -> input.close());

serverCallStreamObserver.setOnCloseHandler(() -> input.close());

  • 缓冲区管理不当:原始代码在判断 serverCallStreamObserver.isReady() 一次后,立即写入所有数据,导致系统缓冲区被大量占用,内存迅速耗尽。
  • 回调机制:通过设置 serverCallStreamObserver.setOnReadyHandler 回调函数,确保只有在缓冲区准备好时才写入数据。这样可以避免一次性写入过多数据,导致内存占用过高。
  • 状态管理:使用 upto 变量跟踪传输偏移量,每次 isReady 时仅写入一部分数据,缓解系统缓冲区的压力。
  • 资源释放:通过 setOnCancelHandlersetOnCloseHandler 确保在取消或关闭时正确关闭输入流,避免资源泄漏。

这种写法会比较反直觉,通过回调函数关闭InputStream看上去没有finally去关闭安全,好处是缓冲区压力小,最终问题解决。

总结加餐

以上描述的问题的根本原因在于 gRPC 在文件传输过程中缓冲区管理不当,导致内存占用过高。通过引入官方推荐的回调机制和合理的状态管理,成功解决了该问题。因此在日常开发中,涉及到文件写入与读取时,还是需要多关注一下流的关闭与打开与缓冲区buffer的使用是否合理。

Linux将物理内存分为内存段,叫做页面。交换是指内存页面被复制到预先设定好的硬盘空间(叫做交换空间)的过程,目的是释放这份内存页面。物理内存和交换空间的总大小是可用的虚拟内存的总量。我们知道swap space是磁盘上的一块区域,可以是一个分区,也可以是一个文件,或者以它们的组合方式出现。简单点说,当系统物理内存吃紧时,Linux系统会将内存中不常访问的数据保存到 swap 上,这样系统就有更多的物理内存为其他进程服务,而当系统需要访问swap上存储的内容时,系统会再将 swap 上的数据加载到内存中,这就是我们常说的swap outswap in了。那么配置多大的 Swap 比较合适?

  • 当物理内存小于1G且不需要休眠时,设置和内存同样大小的swap空间即可;当需要休眠时,建议配置两倍物理内存的大小,但最大值不要超过两倍内存大小。
  • 当物理内存大于1G且不需要休眠时,建议大小为sqrt(RAM),其中RAM为物理内存大小;当需要休眠时,建议大小是RAM+round(sqrt(RAM)),但最大值不要超过两倍内存大小。
  • 如果两倍物理内存大小的swap空间还不够用,建议增加内存而不是增加swap

此外,在一篇参考文献中看到如下这段话:

 如果频繁的访问 swap 的话,怎么优化 swap 都没用,跟内存比还是低几个数量级,性能还是下降的厉害,如果不频繁访问 swap 的话,优化 swap 又有啥意义呢?所以其实优化 swap 性能的实际意义不大。

参考文档

https://zhuanlan.zhihu.com/p/467976849

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/42046.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

static的理论学习

在说到static之前&#xff0c;需要先明确变量类型&#xff1a; 而在聊到变量类型之前我们可以将变量的两个属性好好学一学 变量的两个属性 作用域&#xff08;scope&#xff09;&#xff1a; 从内存的角度来看&#xff0c;就是变量存放在栈&#xff08;stack&#xff09;中&…

TypeError: Cannot read properties of null (reading ‘nextSibling‘)

做项目用的Vue3Vite, 在画静态页面时&#xff0c;点击菜单跳转之后总是出现如下报错&#xff0c;百思不得其解。看了网上很多回答&#xff0c;也没有解决问题&#xff0c;然后试了很多方法&#xff0c;最后竟然发现是template里边没有结构的原因。。。 原来我的index.vue是这样…

ELK+Filebeat+Kafka+Zookeeper

本实验基于ELFK已经搭好的情况下 ELK日志分析 架构解析 第一层、数据采集层 数据采集层位于最左边的业务服务器集群上&#xff0c;在每个业务服务器上面安装了filebeat做日志收集&#xff0c;然后把采集到的原始日志发送到Kafkazookeeper集群上。第二层、消息队列层 原始日志发…

Matlab手搓线性回归-非正规方程法

原理&#xff1a;wxb&#xff0c;x是输入&#xff0c;求得的结果与真实值y求均方误差。 采用链式法则求导 参数更新&#xff0c;梯度下降法&#xff08;批量梯度下降&#xff09; 随机生成数据&#xff1a; m100&#xff1b;生成100个数据&#xff0c;并添加随机噪声 clear; …

基于flask的猫狗图像预测案例

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️如遇文章付费&#xff0c;可先看…

二次元转向SLG,B站游戏的破圈之困

文 | 螳螂观察 作者 | 夏至 2023年是B站游戏的滑铁卢&#xff0c;尽管这年B站的游戏营收还有40多亿&#xff0c;但相比去年大幅下降了20%&#xff0c;整整少了10亿&#xff0c;这是过去5年来的最大跌幅&#xff0c;也是陈睿接管B站游戏业务一年以来&#xff0c;在鼻子上碰的第…

鸿蒙语言基础类库:【@ohos.process (获取进程相关的信息)】

获取进程相关的信息 说明&#xff1a; 本模块首批接口从API version 7开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。…

昇思13天

ResNet50迁移学习 ResNet50迁移学习总结 背景介绍 在实际应用场景中&#xff0c;由于训练数据集不足&#xff0c;很少有人会从头开始训练整个网络。普遍做法是使用在大数据集上预训练得到的模型&#xff0c;然后将该模型的权重参数用于特定任务中。本章使用迁移学习方法对Im…

imx6ull/linux应用编程学习(13) CMAKE

什么是cmake&#xff1f; cmake 工具通过解析 CMakeLists.txt 自动帮我们生成 Makefile&#xff0c;可以实现跨平台的编译。cmake 就是用来产生 Makefile 的工具&#xff0c;解析 CMakeLists.txt 自动生成 Makefile&#xff1a; cmake 的使用方法 cmake 就是一个工具命令&am…

怎么将aac文件弄成mp3格式?把aac改成MP3格式的四种方法

怎么将aac文件弄成mp3格式&#xff1f;手头有一些aac格式的音频文件&#xff0c;但由于某些设备或软件不支持这种格式&#xff0c;你希望将它们转换成更为通用的MP3格式。而且音频格式的转换在现在已经是一个常见且必要的操作。aac是一种相对较新的音频编码格式&#xff0c;通常…

大模型增量预训练新技巧-解决灾难性遗忘

大模型增量预训练新技巧-解决灾难性遗忘 机器学习算法与自然语言处理 2024年03月21日 00:02 吉林 以下文章来源于NLP工作站 &#xff0c;作者刘聪NLP NLP工作站. AIGC前沿知识分享&落地经验总结 转载自 | NLP工作站 作者 | 刘聪NLP 目前不少开源模型在通用领域具有不错…

el-scrollbar实现自动滚动到底部(AI聊天)

目录 项目背景 实现步骤 实现代码 完整示例代码 项目背景 chatGPT聊天消息展示滚动面板&#xff0c;每次用户输入提问内容或者ai进行流式回答时需要不断的滚动到底部确保展示最新的消息。 实现步骤 采用element ui 的el-scrollbar作为聊天消息展示组件。 通过操作dom来实…

理解算法复杂度:空间复杂度详解

引言 在计算机科学中&#xff0c;算法复杂度是衡量算法效率的重要指标。时间复杂度和空间复杂度是算法复杂度的两个主要方面。在这篇博客中&#xff0c;我们将深入探讨空间复杂度&#xff0c;了解其定义、常见类型以及如何进行分析。空间复杂度是衡量算法在执行过程中所需内存…

昇思25天学习打卡营第19天|Diffusion扩散模型

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) Diffusion扩散模型 本文基于Hugging Face&#xff1a;The Annotated Diffusion Model一文翻译迁移而来&#xff0c;同时参考了由浅入深了解Diffusion Model一文。 本教程在Jupyter Notebook上成…

昇思MindSpore学习笔记5-02生成式--RNN实现情感分类

摘要&#xff1a; 记录MindSpore AI框架使用RNN网络对自然语言进行情感分类的过程、步骤和方法。 包括环境准备、下载数据集、数据集加载和预处理、构建模型、模型训练、模型测试等。 一、概念 情感分类。 RNN网络模型 实现效果&#xff1a; 输入: This film is terrible 正…

放大镜案例

放大镜 <!DOCTYPE html> <html lang"zh-cn"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>商品放大镜</title><link rel&qu…

如何使用allure生成测试报告

第一步下载安装JDK1.8&#xff0c;参考链接JDK1.8下载、安装和环境配置教程-CSDN博客 第二步配置allure环境&#xff0c;参考链接allure的安装和使用(windows环境)_allure windows-CSDN博客 第三步&#xff1a; 第四步&#xff1a; pytest 查看目前运行的测试用例有无错误 …

如何使用 pytorch 创建一个神经网络

我已发布在&#xff1a;如何使用 pytorch 创建一个神经网络 SapientialM.Github.io 构建神经网络 1 导入所需包 import os import torch from torch import nn from torch.utils.data import DataLoader from torchvision import datasets, transforms2 检查GPU是否可用 dev…

Yolov10训练,转化onnx,推理

yolov10对于大目标的效果好&#xff0c;小目标不好 一、如果你训练过yolov5&#xff0c;yolov8&#xff0c;的话那么你可以直接用之前的环境就行 目录 一、如果你训练过yolov5&#xff0c;yolov8&#xff0c;的话那么你可以直接用之前的环境就行 二、配置好后就可以配置文件…

前端JS特效第24集:jquery css3实现瀑布流照片墙特效

jquery css3实现瀑布流照片墙特效&#xff0c;先来看看效果&#xff1a; 部分核心的代码如下(全部代码在文章末尾)&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8" /> <title>jquerycss3实现瀑…