51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

测试开发精英班,通向高级软件测试工程师【好消息】企业内训服务上线啦!项目为王,自动化测试提升加速器 !横扫BAT,Python全栈测试开发技能大全
【第119期】:如何快速成长为一个优秀的测试工程师?参与调查问卷 缔造行业趋势 月薪15K+的测试开发必备技能? 【活动】为视频UP主打CALL,互动领福利!
查看: 124|回复: 0

[原创] 性能测试之性能调优 《二》

[复制链接]
  • TA的每日心情
    开心
    3 天前
  • 签到天数: 4 天

    连续签到: 1 天

    [LV.2]测试排长

    发表于 2021-4-8 10:18:39 | 显示全部楼层 |阅读模式
    本帖最后由 德实赋值 于 2021-4-8 10:21 编辑

    四、常见问题及优化方法
    1、SQL执行时间长
    问题现象:系统响应时间长、数据库cpu高。
    问题原因: 全表扫描、索引低效、排序溢出。
    解决方法:
    通过数据库快照查看执行时间长的SQL,查看该SQL执行计划,在cost比较高的SQL上增加合适索引。要求所有大表SQL执行计划的cost低于100,超过100的SQL要评审。对于排序溢出、可参考数据中心规范设置排序堆大小,规范中排序堆设置为AUTOMATIC。
    制定数据库表清理策略。根据数据的生命周期要求,对流水类的数据定期进行清理备份,不再长期保留。定期对全库的表结构进行reorg、runstats操作,以提高索引效率。
    排查方法:
    获得数据库快照(DB2数据库为例):db2 get snapshot for all on corpdb >gswyzfzzshpl1207.log
    从快照中提取慢SQL,Toad查看SQL执行计划
    Db2命令方式查看SQL执行计划:db2expln -d corpdb -t -g -q “SQL语句”
    执行计划查看方法:自上而下查看cost最大的分支,找到未走索引或索引使用不当的表
    2、数据库出现死锁
    问题现象:数据库快照检测到存在数据库死锁,或通过db2evmon -db corpdb -evm DB2DETAILDEADLOCK>dlock.txt生成死锁监控文件,快照或监控文件中存在deadlock的情况。
    问题原因:全表扫描、大事务、更新相同表记录的SQL执行顺序交叉等。
    解决方法:缩短事务路径长度,避免全表扫描。如果必须存在大事务,则更新相同表的SQL执行顺序一致,并且坚决避免全表扫描。网银系统指令发送功能全表扫描+全局大事务,导致数据库死锁。
    排查方法:根据死锁监控文件dlock.txt找到导致死锁的SQL,以及该SQL持有的锁,分析该SQL可能存在的问题。
    3、线程阻塞在日志记录上
    问题现象:系统响应时间长、通过javacore查看很多线程阻塞在打印日志上。
    问题原因:log4j1.x版本较低,性能较差;大报文日志多次输出。
    解决方法:
    减少无效日志、删除无用日志,减少大日志输出。
    升级log4j组件到log4j2,参考log4j2官方文档,配置合理的日志缓冲区,采用高效的Appenders,比如RollingRandomAccessFile。但log4j2仍然采用同步日志,不采用异步日志。如果日志量少(压测产生日志的速度,低于日志写入文件的速度),则可以使用异步日志,大幅提高性能。如果日志量较大,则不建议使用异步日志。
    排查方法:
    JVM启动参数中增加-XX:+HeapDumpOnCtrlBreak,压测进行时,kill -3 pid 杀几个javacore,使用jca457.jar工具打开并分析。推荐使用该工具,因为该工具可以对所有线程状态进行统计,并生成饼状图,方便查看。
    压测进行时,使用jvisualvm获取jvm快照,分析线程堆栈。
    4、多线程并发问题
    问题现象:采用合理的并发数压测,系统出现逻辑错误、交易失败或异常报错。经查是由于对象中变量被异常修改导致。
    问题原因:系统中全局对象中的类变量或全局对象,被多个线程修改。
    解决方法:排查系统中所有持有全局对象或类变量的代码,检查其全局变量是否可能被多个线程并行修改。
    修改方法:
    将全局变量转成方法内的局部变量;
    对全局变量进行同步控制比如syncronized代码块,或者java.util.concurrent锁。
    排查方法:并发问题很可能是由全局变量或者对象导致,准确识别全局变量,通过阅读代码找问题。建议应用梳理所有可能存放全局对象的代码,统一管控,或者把所有全局对象放到一个类中,方便管理。
    5、打开了太多文件
    问题现象:采用合理的并发数压测,交易失败,或后台日志报错:To many open files。
    问题原因:
    读取配置文件或者业务数据文件后,未关闭文件流;
    /etc/security/limits.conf中最大打开文件数配置过小。
    解决方法:
    使用lsof –p pid 命令查看进程打开的文件,如果大部分文件都是同一类型的文件,说明可能未关闭文件流。找到打开文件的代码,关闭文件流即可。
    如果不存在未关闭文件流的问题,且业务本身就需要处理大量文件,则修改/etc/security/limits.conf文件如下内容:
    ·                   hard    nproc           10240
    ·         
    o   1

    ·                   soft    nproc           10240
    ·         



    德实赋值

    6、内存泄漏
    问题现象:JVM内存耗尽,后台日志抛出OutOfMemeryError异常 ;
    问题原因:内存溢出问题可能的原因比较多,可能是全局的List、Map等对象不断被扩大,也可能是程序不慎将大量数据读到内存里;可能是循环操作导致,也可能后台线程定时触发加载数据导致。
    解决方法:对于ibmjdk纯java应用,在jvm启动时设置-XX:+HeapDumpOnOutOfMemory Error参数,会在内存溢出时生成heapdump文件。使用ha456.jar工具打开heapdump文件,分析大对象是如何产生的。
    当然,在heapdump中对象类型可能只是List这种结构,看不出具体哪个业务代码创建的对象。此时要分析所有的全局对象,列出可疑的List或Map对象,排查其溢出原因。
    全局对象、引用的初始化、修改要慎重。建议应用梳理所有可能存放全局对象的代码,统一管控。
    7、JVM垃圾回收频繁
    问题现象:top –H –p pid命令查看,GC Slave线程CPU占用排名始终为前三名,同时Jconsole查看jvm内存占用较高,垃圾回收频繁。使用ga456.jar分析gc日志,查看gc频率、时长。
    打印gc日志的方法:在jvm启动参数里增加-verbose:gc -Xverbosegclog:/usr/ebank/logs/cobp/gcdetail.log
    问题原因:高并发下,内存对象较多,jvm堆内存不够用 。
    解决方法:扩大堆内存大小–Xmx2048m –Xms2048m。
    8、CPU高
    问题现象:50并发压测,监控工具显示bp、前置CPU占用90%以上。
    问题原因:业务处理中存在大量CPU计算操作。
    解决方法:采用更高效的算法、数据结构替换原来消耗CPU的代码,或者采用新的设计绕过瓶颈代码,比如查找数据的逻辑,可以把List改为Map,以空间换时间;比如用Json报文替换XML报文,提高传输、解析和打印日志的效率。
    导致Cpu计算资源高消耗的代码:报文格式转换、加解密、正则表达式、低效的循环、低效的正则表达式。
    排查方法:
    压测进行时,使用jvisualvm工具远程连接应用,点抽样器àCPU,点快照生成线程快照。采样一段时间后,抽样器会显示各个方法占用cpu时间,可以针对CPU时间占用高的方法进行优化。
    使用tprofiler,jprofiler,OracleDeveloperStudio12.6-linux-x86工具分别分析消耗CPU时间长的方法,以上工具分析结果可能有些差别。针对CPU计算耗时最长的方法进行优化。
    9、批处理时间长、数据库逐笔插入缓慢
    问题现象:大批量数据(10万条以上)更新或插入数据库,耗时较长。
    问题原因:批量数据处理时,如果逐条更新数据库,则会存在大量网络io、磁盘io,耗时较长,而且对数据库资源消耗较大。
    解决方法:
    采用java提供的batchUpdate方法批量更新数据库,每1000条commit一次,可大幅提高数据更新效率。
    单线程改成线程池,并行处理,充分利用多核CPU,通过数据库或者其他同步锁控制并行性;增加缓冲池,降低数据库或磁盘IO访问频次。
    10、数据库CPU高
    问题现象:后台指令发送满负荷工作时,数据库CPU高。
    问题原因:后台指令发送线程每次对全量查询结果排序,结果集很大,然后取一条记录;索引区分度不高,满负荷执行时;查询频率很高;压测显示,并行发送指令的后台线程越多,数据库CPU越高,效率越低。
    解决方法:
    去掉ORDER BY,增加索引后,效果不明显。因为结果集大和查询频繁两个问题没有解决,因此考虑使用设计新的方案。
    新方案:设计指令发送线程池,生产者线程每台任务服务器只有一个线程,负责查询待发送指令,每次查询50条指令。每条指令包装成一个Runnable对象,放进ThreadPoolExecutor线程池,线程池大小参数设置为100或200。每当线程池满时,生产者停止生产指令,休息15秒后继续。消费者线程即线程池里的线程,参数设置为4,8或12(和不同指令类型的指令数据量成正比)。
    改进后的方案,数据库CPU降到10%一下,发送效率单机提升6倍,且可线性扩展任务服务器。
    11、压测TPS曲线剧烈下降或抖动
    问题现象:50并发压测,TPS曲线正常应该是平缓的,波动不大,如果突然出现剧烈下降,并且短时间内无法恢复,则可能存在问题。
    问题原因:一般是由于前置或bp的jvm进行垃圾回收,或者日志记录磁盘满导致的。
    解决方法:如果不是特别剧烈的波动或者TPS曲线下降后长时间不反弹,则可以忽略该问题。否则,需要分析曲线下降的时刻,系统当时正在发生的事情。可以通过top命令监控当时CPU占用比价高的线程,也可以kill -3pid杀javacore来查看线程堆栈。
    五、建议优化方案5.1 软件架构
    a. 分层与解耦
        b. 使用缓存
        c. 读写分离
        d. 负载均衡
        e. 区分核心业务与非核心业务
    5.2 业务流程
    单次请求
    1.Request请求BodyResponse响应Body的数据应该尽可能少
    2.请求链路应该尽可能短/依赖尽可能少
    3. ResponseBody中应该尽可能减少额外的依赖
    并发请求
    1. 将不存在顺序依赖的多个请求合并为单次请求
    2. 限制最大并发请求量
    5.3 数据库方面
    1、增加缓存的使用
    对于读多写少的数据,可以加载到分布式缓存,降低数据库压力;
    2、精简日志。删除操作访问记录日志表的操作。
    3、合并、精简接口数量,前端缓存数据。
    德实赋值

    评分

    参与人数 1综合技术指数 +10 收起 理由
    lsekfe + 10 神马都是浮云

    查看全部评分

    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2021-4-17 16:40 , Processed in 0.059884 second(s), 28 queries .

    Powered by Discuz! X3.2

    © 2001-2021 Comsenz Inc.

    快速回复 返回顶部 返回列表