IO密集型服务提升性能的三种方法

news2025/1/15 7:25:14

文章目录

  • 批处理
  • 缓存
  • 多线程
  • 总结

  大部分的业务系统其实都是IO密集型的系统,比如像我们面向B端提供摄像头服务,很多的接口其实就是将各种各样的数据汇总起来,展示给用户,我们的数据来源包括Redis、Mysql、Hbase、以及依赖的一些服务方的数据,并不涉及到太多复杂的计算逻辑。在过去的半年中,因为我们数据量和业务复杂性的增长,确实遇到了一些明显的性能问题,分析大部分问题的本质原因就是IO太慢了。 我们系统中最复杂的计算逻辑执行最慢也就微秒级,而调一次数据库最快也得1-2毫秒,有着2-3个数量级的差距。

  然而IO又是业务系统中不可能干掉的操作,但频繁或者错误的使用IO会给系统带来非常明显的性能问题,轻则拖慢接口影响用户体验,重则OOM直接宕机。 针对IO问题带来性能问题,这里我总结了三种方式 批处理、缓存和多线程,虽然看起来是很简单的操作,但还是得在合适的地方正确使用才能发挥出这三种方法的价值。

批处理

  首先是批处理,这里先说一个真实的案例, 在2021年我们在做服务上云过程中,有个接口上云后,时延从原本的50ms左右涨到了150ms,后来排查发现,之前是串行化去调用KMS,这个服务上云后和KMS的服务端出现了跨机房调用,单次KMS的调用时长增长了近0.5ms。 单看这0.5ms确实不算多,但也架不住几十次的串行调用累计到一起,最终出现了100ms的总延时增长。这种接口时延增长大到原来的三倍,用户是很容易感受到的,可能他们的感受就是这应用真卡!

上面这个问题复现起来很简单,其实就一个for循环,串行去调用kms解密数据量。

for (String str : strList) {
   decodedStr = kmsClient.decrypt(str);  // 单次调用需要0.5-1ms,串行100次需要50-100ms
}

  上述代码整体的主要的耗时其实并不是kms对数据解密的过程上(仅需要微秒级),而是请求发送和接收结果数据时数据在网络上传输的耗时,这就取决于双方服务之间的物理距离了,我们大部分服务都是在北京部署,但仍会出现跨机房调用的情况,这个时候网络延时也会增长0.5-1ms。批处理提升IO性能的原理,其实就是用单次网络IO替代掉原有的多次网络IO,IO时长越长,优化效果越显著。 用一个生活中的例子大家更容易理解些,假设你要给家里准备一份晚餐,其中很重要的一步就是去菜市场买菜,你是一样一样买?还是一次性全买齐了? 这就是单次处理和批处理的区别。

  这个性能问题看似简单,其实在实际编程过程中经常犯,稍不留神就大批量串行IO调用,比如在for循环中查库(你是不是已经在脑海中想到自己写的问题代码了)。 如何避免自己在日常编程中出现类似的问题,我总结了一条编程指导经验,那就是 在任何循环中尽量不要产生IO调用,除非你知道自己在做什么。

  当然也不是所有的IO都会产生问题,有些IO非常快,而且你串行的频次也不是很高,贸然将代码改成批处理的逻辑会显著增加代码复杂度,增加维护成本反而得不偿失,所以建议还是根据具体的IO类型和具体需求,评估具体是否要做批处理。以下我给出一些具体的IO类型和单次IO耗时参考值,大家写代码的时候可以关注下。

IO类型耗时备注
SSD固态磁盘随机访问0.1ms目前大部分服务器在使用SSD了,小文件读写的耗时几乎可以不关注,但如果文件非常大时,这里各方的带宽就是瓶颈,耗时也容易快速增长,重点关注大文件。
Redis访问0.1ms简单Redis查询,主要还是在网络上,Redis服务自身处理请求仅几十us,只要不出大key,基本没问题。
mysql查询1-10ms简单查询可以在10ms下,但涉及到复杂查询或者大量数据无索引的情况下,耗时会显著增长。mysql的异常查询是很多业务系统的性能问题主要来源。
HDD机械磁盘随机访问10ms主要磁盘寻道时间,取决于磁盘转速,如果你恰好用了HDD又想读写文件,无论文件大小这部分耗时是一定不能忽略的。
调用第三方服务1-100ms取决于依赖方的接口性能,不同接口延时的方差非常大,调用第三方接口,性能和容量都需要非常仔细的评估。
同城跨机房RTT0.5ms-
物理距离每增加50-100公里rtt +1ms延时主要来源于光在光纤中的传播耗时+交换机和路由器的处理耗时,比如从广州到北京,一个RTT就需要50ms,对接外部服务接口,如果关注性能,物理距离一定要考虑进去。

缓存

  高IO的应用有个特点,就是大量的数据其实是被重复加载的,这也是”局部性“的一个体现,局部性告诉我们,只有少量的数据会被大量的加载。 利用局部性,我们只要将重要的小部分数据缓存起来,就可以减少大量的IO,从而提升我们系统的性能。如果我们用平均延时来评估性能,我们可以用一个平均延迟计算公式来描述加缓存后的性能:

avgLatency = hitRate * cacheLatency +  (1 - hitRate) * originalLatency

  其中avgLatency代指加了缓存后的平均延迟,hitRate表示缓存的命中率,cacheLatency指的是访问一次缓存所需要的耗时,在实际使用中,如果我们使用了本地缓存,我们可以简单粗暴认为cacheLatency是0,以上公式就可以简化为avgLatency = (1 - hitRate) * originalLatency 。 从简化后的公式可以看出加缓存后的效果仅跟缓存的命中率有关系,如果cache命中率是90%,就会有10倍的性能提升,如果是99%就会有100百性能提升(简略计算),只要我们无限提升缓存命中率,似乎就能无限提升性能。那命中率又和什么相关呢? 答案就是数据的分布、缓存的大小和数据的淘汰策略三者相关。
在这里插入图片描述

数据分布: 现实世界中,大部分数据的访问都受局部性的影响,用大白话讲就是只有少部分数据会被频繁访问,如果把数据被访问频次曲线画出来,如上图。
缓存大小: 这个很好理解,只要缓存的数据足够多,缓存命中率就越高。
淘汰策略: 淘汰策略是指在缓存容量不足的情况下,如何剔除价值最低的数据,常见的淘汰策略有LRU、LFU、FIFO,我们实际情况中用的最多的就是LRU。

  正确考虑到以上三点后,我们大部分情况下是可以将少量高频被访问的数据缓存起来,从而提升系统性能。使用Cache有个额外需要注意的一项就是数据一致性,在cache的使用过程中缓存命中率和数据一致性几乎就是相悖的,很难做到两全其美,就比如我在上篇文章《从CPU的视角看 多线程代码为什么那么难写!》中写道的CPU Cache,其实就是硬件层面使用Cache优化IO性能的一个典型案例,但CPU为保证数据一致性却给当代程序员留下一堆"坑"。

  在实际工作中,关于Cache实现我们有很多选择,常用的比如Guava中的LoadingCache、caffiene、ehcache、redis,spring中也有spring-cache 高级封装,这些如果你都不想用的话,你都可以用Map自己撸一个…… 这里先打个广告,后续关于cache的配置、使用及注意事项会再出一篇详细的文章, 我这里就先不展开了。

多线程

  以上两种方式的本质,其实是通过优化非必要的IO次数来提升性能,但现实情况中并不是所有的IO都可以被优化掉,针对这种情况,其实也就只多线程一条路可选了。这个思路也很好理解,用大白话来说,如果活太多干不完就多招两个人来干。 在IO密集型系统中,多线程的优势在于它能充分利用CPU的计算能力。当一个线程在等待IO操作(如网络请求或磁盘读写)完成时,CPU可以切换到其他线程去执行其他任务,而不是闲置不用。这样,我们就可以充分利用CPU资源,提高系统的响应速度。

  但是,使用多线程并非没有代价。首先,需要注意的是线程切换的开销。如果线程数量过多,线程切换的开销可能会消耗大量的CPU资源。其次,使用多线程会显著增加代码的复杂度,需要考虑到很多并发相关的问题,如:线程间的同步、死锁、资源竞争等,这些都需要在编程时仔细考虑和处理,稍有不慎就会引入很难排查的Bug。

  在Java中,我们可以通过使用ExecutorService、CompletableFuture等工具来创建并管理线程。当然,我们也可以直接使用Thread类来创建线程,但线程需要自行管理,不是很推荐。同时,Java提供了许多同步和并发工具,如synchronized关键字、ReentrantLock、Semaphore等,以帮助我们处理并发问题。

  在多线程优化中,线程池的使用是非常常见的。线程池可以有效地管理和复用线程,避免了频繁地创建和销毁线程所带来的开销。在Java中,我们可以使用ExecutorService来创建一个线程池,然后将任务提交给线程池来执行。在Java8及以上的版本中,我们也可用使用parallelStream()很方便的将代码改造成多线程,但需注意parallelStream底层是使用同一个ForkJoinPool,大量使用可能会出现相互干扰的情况

  另一个常见的多线程优化方式是使用异步编程。异步编程可以让程序在等待IO操作完成的时候,不必阻塞当前线程,而是可以切换到其他任务进行处理。在Java中,我们可以使用Future、CompletableFuture等工具来进行异步编程。
  总的来说,多线程可以是一个强大的工具,可以显著提高IO密集型系统的性能。但是,使用多线程也需要谨慎,需要处理好并发问题,才能确保程序的正确性和稳定性。

总结

  在面对IO密集型系统性能优化时,我们可以通过三种主要的方式来进行:批处理、缓存和多线程。这三种方式各有其优点和适用场景。

  1. 批处理可以通过减少网络IO次数,显著减少网络传输的延迟时间,从而提升系统性能。但是,它需要我们仔细分析和设计我们的数据处理流程,才能找到合适的批处理策略。
  2. 缓存则是通过存储频繁访问的数据,减少了对慢速存储(如磁盘或网络)的访问,从而提升性能。但是,使用缓存时需要考虑数据的一致性问题,以及如何选择合适的缓存淘汰策略。
  3. 多线程则是通过并行处理多个任务,充分利用CPU的计算能力,从而提升性能。但是,使用多线程需要处理并发问题,以及线程管理和调度的开销。

  在实际应用中,这三种方式往往会结合使用,以适应不同的性能需求和系统环境。选择哪种方式,或者如何结合使用,需要根据具体的业务需求、系统环境和性能目标来决定。在进行性能优化时,我们需要深入理解我们的系统,找出性能瓶颈,然后有针对性的进行优化。同时,我们还需要通过性能测试和监控,来验证我们的优化效果,以及及时发现和解决新的性能问题。只有通过这样的方式,我们的系统才能持续提供高效、稳定的服务。

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

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

相关文章

Linux查看GPU显卡/CPU内存/硬盘信息

显卡信息命令/CPU内存/硬盘 1.显卡2、CPU内存3、硬盘 1.显卡 nvidia-smi nvidia-smi(显示一次当前GPU占用情况) nvidia-smi -l(每秒刷新一次并显示) watch -n 5 nvidia-smi (其中,5表示每隔6秒刷新一次终端…

java下载JDK

1.去官网下载 https://www.oracle.com/java/technologies/javase-downloads.html 2.点击 傻瓜式安装 注意选择版本跟电脑系统就行 下载后文件的作用

7.8 SpringBoot事务@Transactional实战 管理员借阅审核

文章目录 前言一、事务是用来干什么的?事务是用来解决什么问题的?二、事务执行的流程三、事务的ACID四大特性四、事务的四种隔离级别五、声明式事务Transactional六、web层实战七、service层实战7.1 BookBorrowService7.2 细数BookBorrowServiceImpl实战细节7.2.1 …

【Linux命令详解 | chmod命令】 chmod命令用于修改文件或目录的权限,保护文件安全性。

文章目录 简介一,参数列表二,使用介绍1. 修改用户权限2. 修改用户组权限3. 修改其他用户权限4. 同时修改多个权限5. 使用数字模式设置权限6. 递归修改目录权限 总结 简介 在Ubuntu系统中,chmod命令是一个强大的工具,用于修改文件…

数据库技术--数据库引擎,数据访问接口及其关系详解(附加形象的比喻)

目录 背景数据库引擎Jet数据库:ISAM:ODBC(Open Database Connectivity): 数据访问接口ADO(ActiveX Data Objects)DAO(Data Access Objects)RDO(Remote Data O…

美团视觉GPU推理服务部署架构优化实战

🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~&#x1f33…

由于找不到vcruntime140.dll,无法继续执行代码,有什么修复方法比较推荐

首先我们在解决vcruntime140.dll问题前,先了解一下vcruntime140.dll是什么,它有什么用途跟作用。 vcruntime140.dll是Microsoft 安装程序的一部分,它是Windows操作系统中的一个动态链接库文件。该文件包含了一些常用的运行时函数和变量&…

Leetcode-每日一题【剑指 Offer 24. 反转链表】

题目 定义一个函数&#xff0c;输入一个链表的头节点&#xff0c;反转该链表并输出反转后链表的头节点。 示例: 输入: 1->2->3->4->5->NULL输出: 5->4->3->2->1->NULL 限制&#xff1a; 0 < 节点个数 < 5000 解题思路 1.题目要求我们反转…

[NOIP2003 普及组] 栈

题目背景 栈是计算机中经典的数据结构&#xff0c;简单的说&#xff0c;栈就是限制在一端进行插入删除操作的线性表。 栈有两种最重要的操作&#xff0c;即 pop&#xff08;从栈顶弹出一个元素&#xff09;和 push&#xff08;将一个元素进栈&#xff09;。 栈的重要性不言自…

【佳佳怪文献分享】MVFusion: 利用语义对齐的多视角 3D 物体检测雷达和相机融合

标题&#xff1a;MVFusion: Multi-View 3D Object Detection with Semantic-aligned Radar and Camera Fusion 作者&#xff1a;Zizhang Wu , Guilian Chen , Yuanzhu Gan , Lei Wang , Jian Pu 来源&#xff1a;2023 IEEE International Conference on Robotics and Automat…

c语言——斐波那契数列应用

//斐波那契数列应用 #include<stdio.h> int main() {int i,n,t10,t21,nextTerm;printf("输出项目数&#xff1a;");scanf("%d",&n);printf("斐波那契数列应用&#xff1a;");for(i1;i<n;i){printf("%d、",t1);nextTermt1…

「C/C++」C/C++搭建程序框架

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「UG/NX」NX二次开发「UG/NX」BlockUI集合「VS」Visual Studio「QT」QT5程序设计「C/C」C/C程序设计「Win」Windows程序设计「DSA」数据结构与算法「File」数据文件格式 目录 术语介绍…

【javaweb】学习日记Day1 - HTML CSS入门

目录 一、图片标签 ① 绝对路径 1.绝对磁盘路径 2.绝对网络路径 ② 相对路径 &#xff08;推荐&#xff09; 二、标题标签 三、水平线标签 四、标题样式 1、CSS引入样式 ① 行内样式 ② 内嵌样式 ③ 外嵌样式 2、CSS选择器 ① 元素选择器 ② id选择器 ③…

74HC595驱动7x11点阵屏(LED-7X11-JHM)DEMO

起因 由于我之前做了一个点阵时钟 &#xff0c;但是无奈LED点阵屏价格比较贵&#xff0c;所以想找一个价格较为便宜的点阵来做便宜一点的点阵方案&#xff0c;再淘宝上看到有那种五毛钱一个的7x11的LED点阵&#xff0c;所以就想着试试搞一下这种点阵屏&#xff0c;这个由于是7…

Ajax 笔记(一)—— Ajax 入门

笔记目录 1. Ajax 入门1.1 Ajax 概念1.2 axios 使用1.2.1 URL1.2.2 URL 查询参数1.2.3 小案例-查询地区列表1.2.4 常用请求方法和数据提交1.2.5 错误处理 1.3 HTTP 协议1.3.1 请求报文1.3.2 响应报文 1.4 接口文档1.5 案例1.5.1 用户登录&#xff08;主要业务&#xff09;1.5.2…

SpringBoot案例-部门管理-根据id查询

目录 根据页面原型&#xff0c;明确需求 查看接口文档 思路分析 接口功能实现 控制层&#xff08;Controller类&#xff09; 业务层&#xff08;Service类&#xff09; 业务类 业务实现类 持久层&#xff08;Mapper类&#xff09; 接口测试 前后端联调 根据页面原型&…

JlinkV8 - 8步修复Jlink固件

现象 用着用着Jlink设备可以检测到&#xff0c;但是MDK检测不到设备序列号&#xff0c;换一个Jlink即可正常识别与烧录&#xff0c;很大概率是Jlink固件丢了&#xff0c;我用的山寨版本&#xff0c;市面基本是山寨版本 解决办法 1、查看Jlink的芯片型号&#xff0c;比如我打开…

练习第30天

选择 多线程可以提高CPU利用率 不能提高内存的利用率 A B A C A B A B C 编程 最难的问题最难的问题__牛客网 #include <iostream> #include <string> #include <unordered_map> using namespace std;//给个字符串翻译出来 // int main() {unordered_…

怎么开通Tik Tok海外娱乐公会呢?

TikTok作为全球知名的社交媒体平台&#xff0c;吸引了数亿用户的关注和参与。许多公司和个人渴望通过开通TikTok直播公会进入这一领域&#xff0c;以展示自己的创造力和吸引更多粉丝。然而&#xff0c;成为TikTok直播公会并非易事&#xff0c;需要满足一定的门槛和申请找cmxyci…

小体积,大能量!邂逅飞凌OKMX6ULL开发板

机缘巧合参加了飞凌嵌入式的试用活动&#xff0c;也很幸运被任命为新品体验官&#xff0c;那么看下是哪一款核心板和底板吧。 →核心板&#xff1a;FETMX6ULL-C核心板 FETMX6ULL-C核心板采用NXP i.MX6ULL处理器开发设计&#xff0c;采用低功耗的ARM Cortex-A7架构&#xff0c…