在以往的项目中,我们针对不同的性能瓶颈进行了专项的性能优化,积累了一些宝贵的经验和教训。这些经验不仅涵盖了具体的技术实现,还包括了在优化过程中遇到的挑战和解决方案。通过对这些优化措施的总结,我们希望能够为未来的项目提供指导,帮助团队更高效地识别和解决性能问题,从而提升系统的整体性能和用户体验。我们将分享这些经验,以期为同行提供参考和借鉴。

优化方向

常见的优化方向:

  • 网络请求
  • IO操作
  • 内存分配
  • 计算层

具体条目

服务层优化

将成本计算和复杂报表等 OLAP 业务移动到新服务以实现读写分离

将一些新服务中的功能在 apollo 中配置化,以动态切换路由到新服务还是老服务

利用 arthas 动态追踪 JVM 问题,而不是修改代码加日志再发布到线上

尽量使用小事务做业务处理,如每个商品在计算成本时都应该使用一个单独的事务执行,避免商品太多导致事务时间太长。事务时间太长会引起数据被锁定无法被其他事务更新,而且数据库连接也会被当成异常连接而被关闭

接口查询大量数据时,建议减少使用for循环查询,少使用大数据量表关联查询,可以先分批查询出主表数据,缩小查询范围,再查询

接口中有在同一张表同时插入数据与读取数据的操作时,应分步进行,再逻辑处理,避免在同一事务中读取到新数据,直接用新数据处理逻辑

少用JSONObject对象传送数据,建议多使用Bean,避免出现问题后dump中都是一堆JSONObject不便于定位

高并发的API要使用队列,减少对系统的冲击

推荐使用性能更好的fastjson,少用或不用net.sf.JSONObject,特别是在序列化大数据量时fastjson性能更佳

高并发下事务内部循环更新数据时,尽量保证循环的有序,降低发生死锁的风险

在使用内存做计算的时候,如果数据量大时可能时间上会比较长,也可能导致内存占用过高,可以利用数据库的资源跑需要做的计算

在我们系统中,controller,service除了必要的数据校验,与合法性校验,其他都可以输出到统一出口处理,并且规范输出错误标准,不得把系统不必要的缺陷输出到前端

数据库优化

基于 apollo 的存储水平分库策略,利用其推拉结合的特性保障数据源的实时性和一致性

oracle 的查询优化也可能犯低级错误(比如命中不了主键),注意其原理是基于代价优化(CBO),这和记录行数/执行历史有直接关系,有时候在开发环境执行100ms在生产可能要执行100s。这种情况要在相应 SQL 加 hint

批量数据更新,使用正确的批量更新代码,只需编译一次sql即可批量更新数据(同一个sql需连续执行)

商品收发报表涉及业务表比较多,用union all 排序计算量比较大,建议把业务聚合到一张表,分表查询

在数据量达到一定的量的时候,导致原本的sql执行很慢,可以结合业务做小sql拆分

在判断数据是否存在的时候不一定需要做count或者非空判断,可以考虑用分页RN第一条数据是否取的到做判断

少用或不用视图,数据库索引尽量覆盖查询条件

缓存优化

基础资料使用本地缓存,如系统属性使用本地缓存;商品单位添加本地缓存,(报表查询中可以在10s未访问则缓存失效)

for访问缓存数据,使用本地缓存(caffeine)

基础资料存入缓存,建议不采用先更新数据库再删除缓存的方式,如果删除缓存失败,会导致数据库与缓存数据不一致,建议先删除缓存再更新数据库,读缓存请求与更新数据库请求建议用队列异步,防止高并发下读取到旧数据

频繁使用的大对象可以采取多级缓存:本地缓存 ->redis缓存 →db

业务逻辑优化

报表一定要分页

有期初余额查询,用使用结存方式,减少计算量(客户收款、供应商应付、商品数据量)

查询一定要限制返回数据量

导出excel文件只支持2007(xlsx)之后,用异步任务导入和导出

其它优化技巧

按照idea提示,或者安装阿里巴巴规范、sonaquab规范插件,不能提交提示有bug的代码;重复代码应尽量抽取成一个方法;应遵从idea提示的最佳代码实现进行编码

运行时指定包日志输出等级,用配置或API修改

慢接口可以通过stopwatch等定位具体哪段代码执行最耗时

补充

数据库

使用多字段in查询提升查询效率

select fmaterialid,fstockid,fqty,fvalidqty 
from t_inv_inventory tii 
where (fmaterialid,fstockid) in (
	(1803544339859877888,1803546190596517888),
	(1737776639233867776,1763668069244353536)
);