性能压测通用策略

性能压测是考验程序员耐性的一件事情,因为会出现很多平时见识不到甚至是感到莫名其妙的事情。2020年中旬,我经历了70产品的一次压测,过程十分曲折,现在誊到博客上,供大家参考吧。

处理思路
根据目前解决的性能压测问题分析,影响代码性能的因素主要划分为代码逻辑问题和sql问题两大类。
在处理性能问题的时候,主要思路如下:
(1)定位问题接口
(2)找到问题接口中的核心处理代码
(3)分析代码逻辑有没有明显问题
(4)分析执行sql是否过慢
 典型问题分析
1、数据库连接池泄露(等级:严重)
(1)问题背景:
在进行登录压测过程中,出现了压测十人就报错,导致压测不能正常进行。后台报错日志如下:

(2)问题处理过程:
1)根据报错,对连接池配置参数进行优化。重新进行压测,问题没有解决。
此过程中发现连接池配置参数写在java代码中为固定值,在优化的同时把此部分配置参数抽取到application.properties配置文件中。后续遇到类似代码需要对会变化的参数进行配置式读取,可以抽取到配置文件或者系统选项中。
2)分析登录逻辑的db使用,发现多处使用db并且存在db被多个方法传递使用的情况。怀疑db使用后没有正常释放、db使用时间过长等问题。
3)修改代码,去除大部分登录逻辑代码,保留一个db的使用进行压测。结果没有出现连接池不够使用的问题。至此确认是登录逻辑中的代码存在连接池泄露的问题。
4)分析登录核心逻辑代码,找到连接池泄露代码,去除后重新进行压测,问题已经解决。连接池泄露代码如下:

(3)问题总结
1)使用Database.java中的新接口execute来创建db替代原有的Database.newInstance(true);的写法,原有的写法存在忘记在finally中关闭db的风险。
2)在进行代码优化过程中遇到原有的newInstance写法需要进行修改,并且在修改的过程中注意删除原有newInstance的逻辑,避免出现上述的问题。

2、S页面打开慢、M页面保存慢(等级:严重)
(1)问题背景:
在压测的需求计划程序数据增加到10w级的时候,出现了S页面打开需要6s、M页面保存需要10s的问题。
(2)问题处理过程:
1)因为数据量比较大,所以初步怀疑是S页面初始化时构建PageControl中pageSize属性时使用select count(*)速度慢导致的。
2)debug跟踪代码,抽取查询总记录数的sql在plsql中执行发现速度很快,不存在问题。
3)将S页面init中的核心方法加上耗时日志,发现其中“初始化参照值字段的options “的方法执行特别慢。并定位到问题sql。
4)分析出错的sql,发现在使用SqlBlock的in方法时如果参数个数超过900时,构建出的sql存在逻辑错误,导致查询出了大量的错误数据。
5)修改SqlBlock的in方法的处理逻辑,问题得到解决。问题sql及修改处理逻辑后的sql如下:
select lin.prtl_id  from pgprtlin lin, pgprtorglnk lnk
 where prt_no = ?
   and lnk.org_no in (1,1,.....)
       or
       lnk.org_no in (1,1,....)
修改后:
select lin.prtl_id  from pgprtlin lin, pgprtorglnk lnk
 where prt_no = ?
   and (lnk.org_no in (1,1,.....)
       or
       lnk.org_no in (1,1,....));
(3)问题总结
1)分析问题时可以通过打日志来进行问题的定位,结合代码debug效果更好。后续打日志除了手动通过Log类的接口外,还应该使用新的日志系统来打日志,包括新的日志注解。
2)在需要使用到in的场景时需要使用SqlBlock的in方法(预编译),不能手动进行拼接。
3)知识点:sql中使用in的时候,后面拼接的参数个数不能超过1000,不然会直接报错。
使用SqlBlock的in方法不存在此问题,已经在内部进行处理。
3、在循环中创建db或者使用db(等级:严重)
不能在循环中创建db,会导致瞬间创建大量db,导致连接池不够使用的问题。
尽量避免在循环中使用db进行操作,可以使用db的批量执行的接口。

4、S页面近期数据指标加载慢(等级:一般)
(1)问题背景:
在优化完S页面初始化后,S页面加载速度正常,但出现grid加载后一会指标才加载的问题。
(2)问题处理过程:
1)F12打开浏览器,查看S页面获取近期数据的接口的耗时为1.2s,而初始化接口的耗时为0.4s。直接定位问题接口。
2)打日志并进行debug定位问题,问题为查询“今日新增”、“最近三日”、“最近一周”的sql慢,共执行了三次,每次耗时0.4s。
3)将查询sql在plsql进行执行并分析。发现三个sql都以创建时间字段FSTUSR_DTM作为查询条件。在此列增加索引后,查询时间减少为0.1左右。
(3)问题总结
进行sql优化的时候,需要对经常进行查询的列增加索引。
5、for循环的使用(等级:一般)
for循环尽量改为增强for循环,foreach的形式。
如一个简单的for循环,实测在10000条数据的情况下,写法一大约耗时在1800-2000ms之间,改为写法二后,耗时会稳定在700-900ms之间,会大大的提升运行效率。
写法一:for (int i = 0; i < list.size(); i++) {
 map.put(list.get(i).getId(), list.get(i));
 }
写法二:for (Menu value : list) {
 map.put(value.getId(), value);
前端优化
1.静态资源压缩处理
目前对前端源码资源文件进行了分类压缩(三方插件不进行合并压缩,直接引用),不同分类部分js资源重复压缩,但是不同类型界面独立引用,相互不干扰,压缩分类如下:
1)非标准界面压缩资源(index.html、home.html、liems自定义开发界面)
2)liems标准程序界面压缩资源
3)sis程序压缩资源
经过测试引用压缩文件标准界面的加载响应速度整体提升25%左右。
2.图片处理
大图片经过压缩后的大小平均减少80%,也极大的提升了界面加载的响应速度。
1)大图片都会进行压缩处理
2)系统较为固定的图片整合为一张图片,通过css进行位移显示
3.基础信息缓存、接口请求合并
合并同类接口,减少接口请求次数,并缓存一些基础信息,从用户登陆界面到系统首页,请求次数减少了40%,极大降低了服务器的压力。
1)用户登陆:用户登陆后,将用户信息、用户设置一次性获取,将这些信息缓存到session中,修改时更新session
2)菜单优化:系统菜单、我的菜单、常用入口等菜单相关接口进行合并一次性请求
3)首页代办:我的代办、我发起的、我审批的接口进行合并请求;快捷操作调整为点击时进行数据请求
4.静态资源缓存策略
服务端使用Cache-Control=no-cache进行缓存控制,前端加载静态资源时候会检查资源的最后修改时间Last-Modified和ETag标记位是否和第一次加载的资源一致,从而进行判断是否加载静态资源,最终达到了降低服务器的压力。
5.主框架弹框独立出html
系统登陆后的主框架所有弹框功能(系统设置、我的菜单、上传头像、切换公司等)全部调整为通过lui.modal.open接口进行逻辑处理,这样就大大减少了主框架的组件渲染,使主框架的初始化更加快,经过测试,加载时间整体提高了40%-50%
6.组件加载顺序优化
1)标准模板界面初始化过程优先加载界面布局,等待布局加载完成后,再进行基本组件和grid组件的加载。
2)Grid组件出现大数据量时,先加载30条记录,再进行循环并且停顿指定时间(10毫秒)加载。
此条优化并没有降低整体加载响应时间,但是达到了分步加载的效果,提前展示出界面布局相关信息,再加载基本组件和grid组件数据,减少了界面的白屏时间,给人感觉程序响应非常快。

猜你喜欢

转载自blog.csdn.net/weixin_38316944/article/details/114395521
今日推荐