背景:
周五快下班时,一线实施反应合约规划单据,成本构成单据体输入金额字段后,系统卡住,一次操作要等10s+后才有响应。
数据量:成本构成单据体有130+行数据(平时正常单据体最多也有10+,看来测试要根据客户实际场景测试,才能更好的以客户为中心【emoji】)
发现问题:
第一反应就是值改变事件中可能做了比较耗时的操作,打开代码,快速找到成本构成单据体-金额字段的值改变事件
隐隐中觉得是这个 "刷新产品构成分录" 的方法可能是性能瓶颈。简单粗暴,先注释这段,然后上测试环境。使用等量级的数据量测试一下。结果确实是这段代码的原因,注释掉后,金额改变事件没有卡顿,很丝滑顺畅。那现在就看看这个方法里面干了什么事情。
这个方法中存在三层for循环嵌套,根据实际数据算一下,(科目139)*(产品6)* (没有楼栋数据) = 834次循环。如果计算逻辑都是在内存中执行循环800+次,那肯定也是毫秒级。因此怀疑可能存在循环查库操作。继续review code排查
第一层循环中的业务代码排查
第二层循环中的业务代码排查
第三层循环中没有发现查库操作
解决问题:
将循环查库操作上提至所有循环外
重构循环中需要查库的方法参数,传入循环前查库的结果集合
验证:
单据体值改变事件明显变快,但是分录行有130+时,响应时间在1~2s之间,如果用户需要在每行快速录入数据时,每次键入完数据后,还是需要一定的等待时间。
持续优化:
一般后台代码性能瓶颈都是在数据库,此时如果还在代码层面优化,也可以,但是成本比较高(此处业务代码算法需要全部翻一遍,优化后,如果对性能改进不大将做了无用功。优化完的代码还需要测试投入测试工作。)显然客户等不了这么久。
改换一种思路,比如业务层面优化。看看这段业务代码干了什么事情,
单据体每个单元格修改时调用该耗费时间(以下使用“坏方法”表示该耗时的方法)的方法
“坏方法”中主要功能是,根据当前单据体所有行的数据,一系列复杂计算后,将结果设置到另外一个单据体(“B单据体”)中
“B单据体”在界面是隐藏的,客户看不到,只有单据审批后,才会被其他单据使用到。
因此,我们可以将“B单据体”的计算过程迁移到“保存”的操作中,不用每次单据体值改变的时候调用。迁移后,单据体录入数据响应时间已经达到毫秒级,保存操作也在2s左后!可以接受!
总结:
避免循环查库,循环前,将需要的数据全部查出。
代码层面性能优化难度变大或者成本变高时,需要转换思路,从业务层面出发,寻找突破点。
千里之行,积于跬步。日常coding中需要提升owner意识,代码规范一定要遵守。
ps : 这篇文章写完用了有半个月时间,基本都是晚上加班抽时间写。虽然花了一些时间只分享了一个小小的性能优化案例,但是总算坚持下来了。后续关于工作中的遇到的问题以及解决方案也会陆续更新。
大家有什么更好的意见或者建议,欢迎交流!
推荐阅读