本期大咖:唐勇
分享主题:JAVA应用性能优化
整理编辑:天搜技术学院
应用性能概述
为何要进行调优
我们在平时的工作中经常碰到项目中出现的一些问题,比如“点击提交订单按钮app怎么自动关闭了”、“商品列表为什么每次都要加载好长时间”、“验证码短信为何过了30分钟还没收到”、“促销活动进行到一半服务器又挂了”等等。一句话,这其实都是开发前期没有考虑全面而挖的坑。让Java应用程序运行是一回事,但让他们跑得快,跑得好就是另外一回事了。在面对对象的环境中,性能问题就像来势凶猛的野兽,所以,这就需要进行应用性能优化,提升应用程序在商业环境中的价值。
应用性能指标
QPS/TPS
QPS(QueriesPerSecond)/TPS(TransactionsPerSecond)原本含义为:系统每秒能处理的请求/事务的数量,或者说吞吐量,这个是衡量系统性能的重要指标
响应时间
请求响应时间指的是从client端发出请求到得到响应的整个时间,响应时间由请求发送时间、网络传输时间和服务器处理时间三部分组成
CPU占用率
指应用服务系统的CPU资源占用率
JVMGC和FullGC
对于java应用的性能指标必定少不了GC的相关指标了。通常我们的应用应该尽量避免FGC。因为FGC会进行完全的垃圾清理,会使应用运行得很慢,所以需要通过设置合适的JVM参数和GC策略来避免FGC。通常监控的指标有GC次数和响应时间
木桶理论又称“短板理论”,其核心思想是:一只木桶盛水的多少,并不取决于桶壁上最高的那块木板,而是取决于桶壁上最短的那块。系统优化就是要把最短的那块板子补上。
优化的一般步骤
注意:性能调优必须有明确的目标。不要为了调优而调优,如果当前程序并没有明显的或可预期的性能问题,盲目地进行调整,其风险可能远远大于收益。
研发经理唐勇在为学员讲解优化的一般步骤
?
优化层次
系统整体视角
设计优化的一大显著特点是,它可以规避某一个组件的性能问题,而非改良该组件的实现。
代码优化虽然是从微观上对性能进行调整,但是一个“好”的实现和一个“坏”的实现对系统的影响也是非常大的。
JVM调优通常可以在软件开发后期进行,由于Java软件总是运行在JVM虚拟机之上,对JVM虚拟机进行优化也能在一定程度上提升Java程序的性能
数据库调优包括对SQL语句进行优化;对数据库设计进行优化;对数据库软件进行优化。
作为软件运行的基础平台,操作系统的性能对应用系统也有较大的影响。不同类型的操作系统,调优的手段和参数可能会有所不同,不是本次重点,暂不讨论。
架构视角
SOA原则和模式
模块化原则和模式
包设计原则和模式
SOLID原则与设计模式
代码规范化及质量
SOLID原则:单一职责,开放封闭,里氏替换,依赖倒置,接口分离
设计模式:工厂模式,单例模式,策略模式,代理模式,观察者模式等23种常用设计模式
SOA原则:标准化服务合约、服务松散耦合、服务抽象、服务可复用性、服务自治、服务无状态性、服务可发现性和服务可组合性
设计开发人员视角
我们开发人员在开发过程中要时候时刻要问自己:
功能设计是否合理
数据库设计是否合理
代码是否存在性能方面的问题
系统中是否有不合理的内存使用方式
系统中是否存在不合理的线程同步方式
系统中是否存在不合理的资源竞争
优化方法
设计调优
案例一:金额处理方式
mysql使用Double或者decimal,java使用Double存储,计算。显示环节都可能出现误差
示例:
Doublea=12.11d;
Doubleb=1.10d;
System.out.println(a+b);
结果:
Java和mysql金额值显示异常处理
问题描述:
数据库金额字段moneydecimal(12,2),存储值mysql客户端显示为.07,直接jdbctemplet模板selectmoneyfrom…查询结果Map返回给客户端变成.,或者hibernate查询得到bean对象直接map.put(account.getMoney())然后retMap转化为json返回客户端显示出来的都是带多个小数位的值(备注:除0.7外其他小数是正常的)
解决方案:
接口返回所有金额字段以字符串形式返回,sql查询和java分别做如下处理。
Sql:
SELECTCONVERT(.,DECIMAL(12,2));#四舍五入(推荐)结果:.14
其他两种sql格式化方式:
SELECTFORMAT(.,2);#逗号分隔结果:4,.14
SELECTTRUNCATE(.,2);#直接截取结果:.13
Java:
DecimalFormatdf=newDecimalFormat(############.00);
objMap.put(totalMoney,df.format(account==null?0:account.getUsable()));
app客户端:
将收到的字符串值转化为两位小数的double数值
研发经理唐勇在为学员讲解设计调优
设计优化:
对于有些要求精确答案的计算任务,请不要使用float或double,如果你希望处理十进制小数点,并且不介意因为不适应原语类型而带来的不便,那么请使用BigDecimal。使用BigDecimal还有一些额外的好处,它允许你完全控制舍入精度;如果你正在进行商务计算,并且要求特别的舍入行为,那么使用BigDecimal是非常方便的,如果性能非常关键,并且你又不介意自己处理十进制小数点,而且所涉及的数值又不太大,那么可以使用int或者Long,如果数值范围超过9位十进制数字,则你可以使用int;如果不超过18位数字,则可以使用long。如果数值范围超过18位数字,你就必须使用BigDecimal。
案例二,树型结构设计
备选设计方案:
1、当前记录记录上一级的主键
优点:最常见方案,简单易用
缺点:mysql不支持递归查询,查询下级全部节点时需循环查询
2、使用两张或三张表分别记录每一级的数据
优点:对层级固定的数据结构支持良好
缺点:不支持层级扩展
3、每一个节点数据使用一个字段记录从根目录开始到当前节点的主键值(逗号分隔)
优点:简单易操作
缺点:层级比较深或者id值比较大的时候字段值比较长
4、对有规律的数据采用限定位数的编码形式
5、MySQL左右值无限分类预排序遍历树算法(适合层级不确定极少更新的场景)
优点:支持无限级,查询速度快,查询结果自带排序
缺点:算法复杂,学习成本高,每次更新节点都要更新节点后面所有记录的左右值
代码调优
代码调优的基础:代码规范
1、类名,方法名,变量名(静态变量大写下划线分隔)必须驼峰命名,见名知意(最好不要出现拼音简写),前后一致;
2、类,方法,变量的注释必须规范详细,复杂逻辑必须给予有意义的注释;
3、类和方法的划分层次必须合理,代码清晰易读,将一个方法里面的独立逻辑提取成方法调用(eclipse:选中要独立成方法的多行代码右键refactoràextractmethod)。
字符串常量拼接编译优化
其他常见优化
for循环中尽量避免定义变量
乘除法运算时尽量使用位运算,尤其是for循环中
多(5个以上)字符串变量拼接使用StringBuilder,并发环境下使用StringBuffer
使用一个对象前考虑是否需要null判断,即该对象在该处是否有可能为空,常见错误:数据库查询出来的list或者在for循环体中list直接调用size()方法
数据库优化
创建表时存储引擎选择
1、使用索引加速查询速度,更新时需要更新索引,因此一张表最多1-3个索引为佳
2、sql性能分析工具:explain显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句
EXPLAINSELECT*FROM`tb_qt_oto_shop`WHEREid=5ANDagent_id=2
复杂查询:
Explain详解:
Id:SELECT识别符。这是SELECT查询序列号。这个不重要,查询序号即为sql语句执行的顺序
select_type:每个查询的类型
Simple:它表示简单的select,没有union和子查询
Primary:最外面的select,在有子查询的语句中,最外面的select查询就是primary
Union:union语句的表示当前查询使用了union合并语句
table:输出的行所用的表或表别名,这个参数显而易见,容易理解
type:显示连接使用的类型,常见类型按速度降序排序
consteq_refrefrangeindexall
possible_keys:指出MySQL能在该表中使用哪些索引有助于查询,即提供索引建议。如果为空,说明没有可用的索引
key:MySQL实际从possible_key选择使用的索引。如果为NULL,则没有使用索引。很少的情况下,MYSQL会选择优化不足的索引。这种情况下,可以在SELECT语句中使用USEINDEX(indexname)来强制使用一个索引或者用IGNOREINDEX(indexname)来强制MYSQL忽略索引
key_len:使用的索引的长度,一般key_len等于索引列类型字节长度,例如int类型为4-bytes,bigint为8-bytes。在不损失精确性的情况下,长度越短越好
ref:显示索引的哪一列被使用了
rows:MYSQL扫描的行数
最后,唐勇表示以上仅是Java应用性能优化的一部分,性能优化大部分都是在时间、效率、代码结构层次等方面的权衡,各有利弊,不要把上面内容当成教条,或许有些对我们实际工作适用,有些不适用,还望根据实际工作场景进行取舍吧,活学活用,变通为宜。
如果大家课后还有什么问题,欢迎北京青少年白癜风医院北京白癜风网上医院