榨干服务器:一次惨无人道的性能优化

【KingbaseES】如何查询连接数 系统表sys_stat_activity字段详解 字段名 字段 数据库名 datname 进程ID pid 连接用户名 usename 数据库名 application_name 数据库名 client_addr selet * from sys_stat_activity; – 查看数据库连接总数 selet count(1) from sys_stat_activity; – 查看数据库某个库连接总数 selet count(1) from sys_stat_ac 阅读详情

背景

做过2B类系统的同学都知道,2B系统最恶心的操作就是什么都喜欢批量,这不,我最近就遇到了一个恶心的需求——50个用户同时每人导入1万条单据,每个单据七八十个字段,请给我优化。

榨干服务器:一次惨无人道的性能优化

Excel导入技术选型

说起Excel导入的需求,很多同学都做过,也很熟悉,这里面用到的技术就是POI系列了。

但是,原生的POI很难用,需要自己去调用POI的API解析Excel,每换一个模板,你都要写一堆重复而又无意义的代码。

所以,后面出现了EasyPOI,它基于原生POI做了一层封装,使用注解即可帮助你自动解析Excel到你的Java对象。

EasyPOI虽然好用,但是数据量特别大之后呢,会时不时地来个内存溢出,甚是烦恼。

所以,后面某里又做了一些封装,搞出来个EasyExcel,它可以配置成不会内存溢出,但是解析速度会有所下降。

如果要扣技术细节的话,就是DOM解析和SAX解析的区别,DOM解析是把整个Excel加载到内存一次性解析出所有数据,针对大Excel内存不够用就OOM了,而SAX解析可以支持逐行解析,所以SAX解析操作得当的话是不会出现内存溢出的。

因此,经过评估,我们系统的目标是每天500万单量,这里面导入的需求非常大,为了稳定性考虑,我们最后选择使用EasyExcel来作为Excel导入的技术选型。

导入设计

我们以前也做过一些系统,它们都是把导入的需求跟正常的业务需求耦合在一起的,这样就会出现一个非常严重的问题:一损俱损,当大导入来临的时候,往往系统特别卡。

导入请求同其它的请求一样只能打到一台机器上处理,这个导入请求打到哪台机器哪台机器倒霉,其它同样打到这台机器的请求就会受到影响,因为导入占用了大量的资源,不管是CPU还是内存,通常是内存。

还有一个很操蛋的问题,一旦业务受到影响,往往只能通过加内存来解决,4G不行上8G,8G不行上16G,而且,是所有的机器都要同步调大内存,而实际上导入请求可能也就几个请求,导致浪费了大量的资源,大量的机器成本。

另外,我们导入的每条数据有七八十个字段,且在处理的过程中需要写数据库、写ES、写日志等多项操作,所以每条数据的处理速度是比较慢的,我们按50ms算(实际比50ms还长),那1万条数据光处理耗时就需要 10000 * 50 / 1000 = 500秒,接近10分钟的样子,这个速度是无论如何都接受不了的。

所以,我一直在思考,有没有什么方法既可以缩减成本,又可以加快导入请求的处理速度,同时,还能营造良好的用户体验?

经过苦思冥想,还真被我想出来一种方案:独立出来一个导入服务,把它做成通用服务。

导入服务只负责接收请求,接收完请求直接告诉前端收到了请求,结果后面再通知。

然后,解析Excel,解析完一条不做其它处理直接就把它扔到Kafka中,下游的服务去消费,消费完了,再发一条消息给Kafka告诉导入服务这条数据的处理结果,导入服务检测到所有行数都收到了反馈,再通知前端这次导入完成了。(前端轮询)

榨干服务器:一次惨无人道的性能优化

如上图所示,我们以导入XXX为例描述下整个流程:

  1. 前端发起导入XXX的请求;
  2. 后端导入服务接收到请求之后立即返回,告诉前端收到了请求;
  3. 导入服务每解析一条数据就写入一行数据到数据库,同时发送该数据到Kafka的XXX_IMPORT分区;
  4. 处理服务的多个实例从XXX_IMPORT的不同分区拉取数据并处理,这里的处理可能涉及数据合规性检查,调用其他服务补齐数据,写数据库,写ES,写日志等;
  5. 待一条数据处理完成后给Kafka的IMPORT_RESULT发送消息说这条数据处理完了,或成功或失败,失败需要有失败原因;
  6. 导入服务的多个实例从IMPORT_RESULT中拉取数据,更新数据库中每条数据的处理结果;
  7. 前端轮询的接口在某一次请求的时候发现这次导入全部完成了,告诉用户导入成功;
  8. 用户可以在页面上查看导入失败的记录并下载;

这就是整个导入的过程,下面就开始了踩坑之旅,你准备好了吗?

聪明的同学会发现,(关注公号彤哥读源码一起学习一起浪)其实大批量导入跟电商中的秒杀是有些类似的,所以,整个过程引入Kafka来在削峰和异步。

初步测试

经过上面的设计,我们测试导入1万条数据只需要20秒,比之前预估的10分钟快了不止一星半点。

但是,我们发现一个很严重的问题,当我们导入数据的时候,查询界面卡到爆,需要等待10秒的样子查询界面才能刷出来,从表象来看,是导入影响了查询。

初步怀疑

因为我们查询只走了ES,所以,初步怀疑是ES的资源不够。

但是,当我们查看ES的监控时发现,ES的CPU和内存都还很充足,并没有什么问题。

然后,我们又仔细检查了代码,也没有发现明显的问题,而且服务本身的CPU、内存、带宽也没有发现明显的问题。

真的神奇了,完全没有了任何思路。

而且,我们的日志也是写ES的,日志的量比导入的量还更大,查日志的时候也没有发现卡过。

所以,我想,直接通过Kibana查询数据试试。

说干就干,在导入的同时,在Kibana上查询数据,并没有发现卡,结果显示只需要几毫秒数据就查出来了,更多的耗时是在网络传输上,但是整体也就1秒左右数据就刷出来了。

因此,可以排除是ES本身的问题,肯定还是我们的代码问题。

此时,我做了个简单的测试,我把查询和导入的处理服务分开,发现也不卡,秒级返回。

答案已经快要浮出水面了,一定是导入处理的时候把ES的连接池资源占用完了,导致查询的时候拿不到连接,所以,需要等待。

通过查看源码,最终发现ES的连接数是在RestClientBuilder类中写死的,
DEFAULT_MAX_CONN_PER_ROUTE=10,DEFAULT_MAX_CONN_TOTAL=30,每个路由最大10,总连接数最大30,而且更操蛋的是,这两个配置是写死在代码里面的,没有参数可以配置,只能通过修改代码来实现了。

这里也可以做个简单的估算,我们的处理服务部署了4台机器,每台机器一共可以建立30条连接,4台机器就是120条连接,导入一万单如果平均分配,每条连接需要处理 10000 / 120 = 83条数据,每条数据处理100ms(上面用的50ms,都是估值)就是8.3秒,所以,查询的时候需要等待10秒左右,比较合理。

直接把这两个参数调大10倍到100和300,(关注公号彤哥读源码一起学习一起浪)再部署服务,测试发现导入的同时,查询也正常了。

接下来,我们又测试了50个用户同时导入1万单,也就是并发导入50万单,按1万单20秒来算,总共耗时应该在 50*20=1000秒/60=16分钟,但是,测试发现需要耗时30分钟以上,这次瓶颈又在哪里呢?

再次怀疑

我们之前的压测都是基于单用户1万单来测试的,当时的服务器配置是导入服务4台机器,处理服务4台机器,根据上面我们的架构图,按理说导入服务和处理服务都是可以无限扩展的,只要加机器,性能就能上去。

所以,首先,我们把处理服务的机器加到了25台(我们基于k8s,扩容非常方便,改个数字的事),跑一下50万单,发现没有任何效果,还是30分钟以上。

然后,我们把导入服务的机器也加到25台,跑了一下50万单,同样地,发现也没有任何效果,此时,有点怀疑人生了。

通过查看各组件的监控,发现,此时导入服务的数据库有个指标叫做IOPS,已经达到了5000,并且持续地在5000左右,IOPS是什么呢?

它表示一秒读写IO多少次,跟TPS/QPS差不多,说明MySQL一秒与磁盘的交互次数,一般来说,5000已经是非常高的了。

目前来看,瓶颈可能在这里,再次查看这个MySQL实例的配置,发现它使用的是超高IO,实际上还是普通的硬盘,想着如果换成SSD会不会好点呢。

说干就干,联系运维重新购买一个磁盘是SSD的MySQL实例。

切换配置,重新跑50万单,这次的时间果然降下来了,只需要16分钟了,接近降了一半。

所以,SSD还是要快不少的,查看监控,当我们导入50万单的时候,SSD的MySQL的IOPS能够达到12000左右,快了一倍多。

后面,我们把处理服务的MySQL磁盘也换成SSD,时间再次下降到了8分钟左右。

你以为到这里就结束了嘛(关注公号彤哥读源码一起学习一起浪)?

思考

上面我们说了,根据之前的架构图,导入服务和处理服务是可以无限扩展的,而且我们已经分别加到了25台机器,但是性能并没有达到理想的情况,让我们来计算一下。

假设瓶颈全部在MySQL,对于导入服务,我们一条数据大概要跟MySQL交互4次,整个Excel分成头表和行表,第一条数据是插入头表,后面的数据是更新头表、插入行表,等处理完了会更新头表、更新行表,所以按12000的IOPS来算的话,MySQL会消耗我们 500000 * 4 / 12000 / 60= 2.7分钟,同样地,处理服务也差不多,处理服务还会去写ES,但处理服务没有头表,所以时间也按2.7分钟算,但是这两个服务本质上是并行的,没有任何关系,所以总的时间应该可以控制在4分钟以内,因此,我们还有4分钟的优化空间。

再优化

经过一系列排查,我们发现Kafka有个参数叫做
kafka.listener.concurrency,处理服务设置的是20,而这个Topic的分区是50,也就是说实际上我们25台机器只使用了2.5台机器来处理Kafka中的消息(猜测)。

找到了问题点,就很好办了,先把这个参数调整成2,保持分区数不变,再次测试,果然时间降下来了,到5分钟了,后面经过一系列调整测试,发现分区数是100,concurrency是4的时候效率是最高的,最快可以达到4分半的样子。

至此,整个优化过程告一段落。

总结

现在我们来总结一下一共优化了哪些地方:

  1. 导入Excel技术选型为EasyExcel,确实非常不错,从来没出现过OOM;
  2. 导入架构设计修改为异步方式处理,参考秒杀架构;
  3. Elasticsearch连接数调整为每个路由100,最大连接数300;
  4. MySQL磁盘更换为SSD;
  5. Kafka优化分区数和kafka.listener.concurrency参数;

另外,还有很多其它小问题,限于篇幅和记忆,无法一一讲出来。

后期规划

通过这次优化,我们也发现了当数据量足够大的时候,瓶颈还是在存储这块,所以,是不是优化存储这块,性能还可以进一步提升呢?

答案是肯定的,比如,有以下的一些思路:

  1. 导入服务和处理服务都修改为分库分表,不同的Excel落入不同的库中,减轻单库压力;
  2. 写MySQL修改为批量操作,减少IO次数;
  3. 导入服务使用Redis来记录,而不是MySQL;

但是,这次要不要把这些都试一遍呢,其实没有必要,通过这次压测,我们至少能做到心里有数就可以了,真的等到量达到了那个级别,再去优化也不迟。

好了,今天的文章就到这里了

MyBatis-Plus+EasyPOI大量数据导出 MyBatis-Plus的入门 Pom依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:sch... 阅读详情

相关推荐

看惨无人道的性能优化如何榨干服务器

背景 做过2B类系统的同学都知道,2B系统最恶心的操作就是什么都喜欢批量,这不,我最近就遇到了一个恶心的需求——50个用户同时每人导入1万条单据,每个单据七八十个字段,请给我优化。 Excel导入技术选型 说起Excel导入的需求,很多同学都做过,也很熟悉,这里面用到的技术就是POI系列了。 但是,原生的POI很难用,需要自己去调用POI的API解析Excel,每换一个模板,你都要写一堆重复而又无意义的代码。 所以,后面出现了EasyPOI,它基于原生POI做了一层封装,使用注解即可帮助你

m0_67645544的博客 322

elasticsearch之配置详解

# ------------------------------------ Node ------------------------------------ #集群名字,同一网段内相同名字加入同一集群       cluster.name: test-es #节点名字    node.name: node-1 #节点是否为主节点    node.master: true #节点是

归去来兮 3078

大数据Excel操作不会内存溢出POI

绝对原创,这是我在项目中解决大数据Excel导入时内存溢出问题而编写的Excel行级解析器。同时支持Excel-2003和Excel-2007,excel-2003解析采用poi的eventusermodel模式实现,2007采用xmlreader实现,经项目验证,excel文件20W条数据没有任何问题,仅读取的话2-3分钟解析完毕。 网络上有关eventusermodel方式解析的资料太少,我花费时间较多,因此分数较高,但绝对物有所值!源码中有示范解析的测试类,简单易懂。 源码运行所需要的lib包,参见 http://download.youkuaiyun.com/detail/whatismvc/3694135

elasticsearch学习(七):es客户端RestHighLevelClient

Java Low Level REST Client:ES官方的低级客户端。低级别的客户端通过http与Elasticearch集群通信。Java High Level REST Client:ES官方的高级客户端。基于上面的低级客户端,也是通过HTTP与ES集群进行通信。它提供了更多的接口。客户端(Client) Jar包的版本尽量不要大于Elasticsearch本体的版本,否则可能出现客户端中使用的某些API在Elasticsearch中不支持。

weixin_40482816的博客 4万+

中兴FTTR光猫变简易OLT:低成本搭建全光监控与局域网实战指南

本文详细介绍了如何将中兴FTTR光猫改造为简易OLT设备,实现低成本全光监控与局域网扩展。通过LOID认证、桥接模式设置及终端数破解,用户可利用二手设备搭建高性能光纤网络,适用于远程监控和长距离组网场景,显著降低部署成本。

chair的专栏 2035

深入解析MTK DRM初始化流程:从KMS模块到组件框架

本文深入解析了联发科(MTK)平台上DRM显示框架的初始化流程。从内核模块加载、Component框架管理依赖,到核心的mtk_drm_bind函数构建KMS对象(CRTC、Plane、Encoder、Connector),最终完成DRM设备注册并创建用户空间接口。文章结合调试经验,为开发者梳理了从驱动启动到屏幕点亮的完整路径与关键排查点。

onion的博客 767

2021-02-20

搭建Es集群 第一步准备环境 1.关闭selinux setenforce 0 sed -i s/enforcing/disabled/g /etc/selinux/config 2.关闭防火墙 systemctl start firewalld systemctl disable firewalld 3创建es的启动用户并且给es设置家目录 groupadd es useradd es -g es -p elasticsearch 4.上传ES的压缩包包并且赋予属主和属主的权限和755的权限 sz /h

weixin_45742674的博客 324

task5:在本地搭建大模型(ollama 和 LM studio 对比),接入 openclaw

关于大模型,大家千万不要想着在本地部署,根本不可行,没有那个财力。另外大模型迭代快,不可能每周都在下载大模型。再次,还是国外的模型好用,国内的模型真的在 deepresearch 上就有很大问题,你们懂的。所以本篇文章,还是聚焦在探索层面+实操,并不会实战。

这里是 AI 食用指南,持续分享有用的AI 实操和应用! 1968

最大连接数

client最大tcp连接数 client每次发起tcp连接请求时,除非绑定端口,通常会让系统选取一个空闲的本地端口(local port),该端口是独占的,不能和其他tcp连接共享。tcp端口的数据类型是unsigned short(2字节),因此本地端口个数最大只有65536,端口0有特殊含义,不能使用,这样可用端口最多只有65535,所以在全部作为client端的情况下,最大tcp连接数为65535,这些连接可以连到不同的server ip。 server最大tcp连接数 server通常固定在某个本

iCode_girl的博客 1851

AM驱动架构—优质Mini-LED显示技术解决方案

(VR)和增强现实(AR)设备:MiniLED背光驱动方案适用于虚拟现实和增强现实设备中的显示技术,通过高亮度和高对比度的显示效果,创造更逼真的虚拟体验此外,MiniLED背光还能够实现更快的响应速度,减少图像残影,使得用户的动作在虚拟环境中更加流畅自然。以其出色的性能和广泛的应用前景备受关注是实现MiniLED显示效果的重要环节。1)更高的亮度和对比度:尺寸更小,可以在同样面积的显示屏上配置更多的LED,并且通过独立控制每一个MiniLED,可以实现更高的亮度和更细致的对比度,使得显示画面更加鲜明清晰。

isweekDN的博客 2763

Arduino 系列:Arduino Due 系列 (基于 ATSAM3X8E)_(5).电源管理与电压调节

电源管理与电压调节是确保嵌入式系统稳定性和可靠性的关键环节。通过合理选择电源输入方式、使用高效的电压调节器、设计适当的滤波电路和实施动态电源管理,可以有效提高系统的性能和寿命。同时,使用电源调试工具和编写调试代码,可以帮助开发者及时发现和解决问题。希望本文档能为使用 Arduino Due 的开发者提供有用的信息和指导。

2401_87715305的博客 1309

es中的网络模块

用于处理es内部节点通信及http请求通信的。类为NetworkModule,其主要管理三个主要的类Transport,HttpServerTransport以及TransportInterceptor。 1、初始化 node启动时,会创建NetworkModule,并且将网络插件传给NetworkModule。 final NetworkModule networkModule = new NetworkModule(settings, pluginsService.filterPlugins(N

多看多听多总结 2844

YOLO模型从10MB瘦到2MB!剪枝+INT8量化双方案(精度损失<1%,附部署代码)

摘要:本文介绍了一种针对边缘设备的YOLO模型轻量化方案,通过"结构化剪枝+INT8量化"组合技术,将YOLOv8n模型从6.4MB压缩至2MB,体积减少68.75%,推理速度提升80%,同时保持精度损失小于1%。文章详细讲解了剪枝工具选择、通道剪枝实现、量化校准等关键步骤,并提供了完整的代码示例。特别强调了剪枝保留率、校准集使用、微调必要性等实践要点,帮助开发者在树莓派等资源受限设备上实现高效目标检测。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 756

关于ES的几个问题

本文来说下关于ES的几个问题 文章目录概述 概述 经常遇到很多朋友询问,如何学好Elasticsearch?这个问题本质上很不好回答,但我一直又很想好好回答,所以本文就以我个人的经验视角,跟大家探讨一下如何正确的拥抱Elasticsearch。若有不当之处,欢迎留言指正。 ...

wh柒八九的博客 1015

多物理场耦合仿真-主题032-电磁-流体耦合

电磁-流体耦合,即磁流体动力学(MHD, Magnetohydrodynamics),是研究导电流体在电磁场中运动规律的学科,涉及电磁场与流体力学的深度耦合。本主题系统阐述磁流体动力学的基本理论、控制方程和数值方法。首先介绍MHD的基本概念和物理基础,包括导电流体的性质、电磁场基本方程;然后详细推导MHD控制方程组,包括连续性方程、动量方程、能量方程和电磁场方程;接着讨论MHD流动的特征参数,如Hartmann数、磁雷诺数、相互作用参数等;最后通过六个典型工程案例展示MHD分析的实际应用,包括电磁泵、磁流体

2401_87715305的博客 424

【微信小程序】微信小程序开发环境搭建:安装微信小程序开发者工具

通过本文的介绍,你应该已经成功安装并配置了微信小程序开发者工具,为微信小程序开发做好了准备。接下来,你可以开始编写你的第一个小程序项目,探索更多有趣的功能和特性。祝你开发顺利,创造更多优秀的作品!

墨夶的博客 2952
上一篇: 公司刚来的阿里p8,看完我构建的springboot框架,甩给我一份文档
下一篇: 程序员必修课:阿里性能优化全解终开源 设计+代码+JVM三飞
Javaesandyou
博客等级 码龄5年 742粉丝 563原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值