复制代码

为懒人提供无限可能,生命不息,code不止

人类感性的情绪,让我们知难行难
我思故我在
日拱一卒,功不唐捐
  • 首页
  • 前端
  • 后台
  • 数据库
  • 运维
  • 资源下载
  • 实用工具
  • 接口文档工具
  • 登录
  • 注册

ERP

【原创】记录一次ERP系统性能优化过程

作者: whooyun发表于: 2024-06-14 11:46

实现方案描述
在商品档案导入页面,用户点击导入一份包含3万条数据的商品档案Excel文件(包含38个字段)。导入时需要进行字段合法性和业务合规性验证,验证完成后,数据将组装成8张表的VO商品数据,并通过中间件RocketMQ发送给商品档案消费者进行保存操作。MQ消费者接收到信息后,会创建20个线程进行商品档案保存,每条线程处理8000条数据,通过一个数据库事务方法batchInsert将数据批量插入数据库表中。

框架中间件说明
项目框架:Spring Boot2.2
数据层:MyBatis-Plus(基于MyBatis 3)
数据库:阿里云 RDS MySQL 5.7
数据库连接池:Druid(默认配置)
消息中间件:RocketMQ(使用官方默认配置)
现场现象
在执行上述操作时,服务器CPU暴涨到300%(多核),内存从配置的最小2GB暴涨到4GB,持续时间超过1小时。调用当前服务的其他接口响应非常缓慢或超时,但同一服务器的其他业务应用未出现异常。

分析处理办法
1.堆栈信息分析:
使用JDK自带工具jstat -gcmetacapacity -h5 PID 1000 20 查看进程的jvm各种性能数据
   MCMN       MCMX        MC       CCSMN      CCSMX       CCSC     YGC   FGC    FGCT     GCT   
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   885     6    1.306   12.991
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   885     6    1.306   12.991
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   885     6    2.306   12.991
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   885     6    3.306   12.991
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   885     6    1.306   12.991
   MCMN       MCMX        MC       CCSMN      CCSMX       CCSC     YGC   FGC    FGCT     GCT   
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   885     6    1.306   12.991
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   885     6    4.306   12.991
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   885     6    1.306   12.991
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   885     6    5.306   12.991
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   885     6    1.306   12.991
   MCMN       MCMX        MC       CCSMN      CCSMX       CCSC     YGC   FGC    FGCT     GCT   
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   885     6    1.306   12.991
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   886     6    6.306   12.999
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   886     6    7.306   12.999
       0.0  1226752.0   201048.0        0.0  1048576.0    23896.0   886     6    8.306   12.999

使用JDK自带工具jstack查看进程的堆栈信息,发现大量TIMED_WAITING线程,堆栈信息如下:
java.lang.Thread.state: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
parking to wait for <0x0009000081d048c8> (a java,util.concurrent, locks.AbstractQueuedsynchronizer$conditionobject)
t iovo util concurrent locle lockSunnort
socell oclsuos
at java,util,concurrent, locks,AbstractOueuedsynchronizer$Condition0biect,await (AbstractQueuedsynchronizer,java:2039)
at com.alibaba.druid.pool.DruidDataSource.takeLast(DruidDataSource.java:2190)
at com.alibaba,druid,pool.DruidDataSource.getconnectionInternal(DruidDataSource.java:1682)
at com.alibaba,druid,pool,DruidDataSource.getconnectionDirect(DruidDataSource.java:1419)
at com.alibaba,druid.pool,DruidDataSource,getConnection(DruidDataSource.java:1399)
at com,alibaba,druid,pool,DruidDataSource.getconnection(DruidDataSource.java:1389)
at com,alibaba.druid.pool,DruidDataSource,getconnection(DruidDataSource.java:100)
at org,apache.shardingsphere,driver. jdbc, core, connection.connectionManager,createconnection(connectionManager. java: 358)
at org,apache ,shardingsphere,driver, jdbc,core, connection, connectionManager,createconnections(connectionManager. java:327)
at org.apache,shardingsphere.driver. jdbc,core.connection,ConnectionManager.getConnections(ConnectionManager. java: 316)
at org,apache ,shardingsphere,infra.executor,sql.prepare.driver,DriverExecutionPrepareEngine.group(DriverexecutionPrepareEngine.java:88)
at org.apache shardingsphere,infra,executor,sql.prepare,AbstractexecutionPrepareEngine,prepare(AbstractExecutionPrepareengine.java: 62)
at org .apache ,shardingsphere.driver, jdbc.core.statement,shardingspherePreparedstatement.createExecutionGroupcontext(shardingspherePrepare
at org,apache.shardingsphere,driver, jdbc,core.statement .ShardingspherePreparedstatement,execute(shardingspherePreparedtatement,java:400)
at sun,reflect.GeneratedMethodAccessorl55,invoke(Unknown source)
at sun. reflect.DelegatingMethodAccessorImpl,invoke(DelegatingMethodAccessorImpl.java: 43)
at iava.lang.reflect.Method.invoke(Method.java:498)
at org.apache. ibatis.logging.jdbc,PreparedstatementLogger,invoke(PreparedstatementLogger.java:59)
at com.sun.proxy.$Proxy244.execute(Unknown Source)
at org.apache.ibatis.executor,statement.PreparedstatementHandler,query(PreparedStatementHandler. java: 64)

2.问题定位:
通过关键词PreparedStatementHandler、getConnections、createConnection,推测是数据库连接获取出现问题,然后线程处于wait状态,又导致了jvm进行FGC,SWT把服务器cpu拉满。考虑到连接无法获取,推测可能是应用的数据库连接池设置过小或MySQL的连接数耗尽。

3.进一步确认:
从运维人员获知,业务场景发生时,MySQL的CPU并未暴涨,连接数也未耗尽。因此,问题极有可能出在项目中的数据库连接池设置过小。查看公司封装的框架,默认最大连接数是8。

4.解决方案:
将数据库连接池的最大活跃连接数调整到100,重启服务,并进行同一个业务场景验证。验证结果显示,CPU和内存有波动,但持续时间非常短(在一两分钟内),且当前业务应用接口能正常访问,无超时现象。

总结
通过调整数据库连接池的最大活跃连接数,成功解决了服务器CPU和内存暴涨的问题,确保了业务操作的正常进行,提高了系统的稳定性和响应速度。
参考资料
阿里分析工具 https://arthas.aliyun.com/en/doc/commands.html