背景:
招聘系统的offer及入职报表目前缺失部分字段,需要补充,但是缺失的字段大多在其他服务里,而且基本都有相互关联关系,普通方法实现之后性能存在问题。同时上线之后导出数据限制从1000->2000,更可能会出现问题,需要进行优化。
需求:
增加字段如下:三级部门负责人 三级部门负责人工号 二级部门负责人 二级部门负责人工号 一级审人 一级审人工号 审批时间 审批内容 二级审人 二级审人工号 审批时间 审批内容 三级审人 三级审人工号 审批时间 审批内容 四级审人 四级审人工号 审批时间 审批内容 .......到十级审批
分析:
1.需要hr的部门服务,员工服务,用户服务
2.需要流程平台的审批单据查询服务,同时每条审批历史都要找到对应员工的工号
报表主表中t_employ_record_bill中有流程服务的processId,每条都能查到1~4条审批记录。
第一版实现
1.获取所有在职员工信息,离职员工信息,部门信息进行list→map
2.循环报表list实体,循环调用流程服务,一个processId查到一个list,然后Map<processId,List<ApproveHistoryDto>>
3.绘制Excel,递归寻找部门负责人和工号,二级循环循环绘制流程审批单元格
根据上面的常规操作进行导出测试,314条数据导出时间如下:
第一版导出实践.jpg
由于故意进行并发访问导出,时间越来越长,基本超过30s,
第二版实现
方案考虑:由于是报表,不要求数据实时性太强,而且审批流审批通过之后不会再有更改,可以考虑缓存。但是数据记录是选择选中的行进行导出,所以无法确定缓存的审批历史数据范围有多少。根据调研线上数据大概11000条,平均每条记录大概有4条审批历史记录,所以
审批历史记录总数大概在44000条左右,我们只需要四个字段,计算一下4个字段合成的List<ApproveHistoryBean>大概需要多少内存,线下测试需要11M~12M,可以考虑缓存到Redis里,但是这个key是大key,所以不太适合,如果单条存储的话也需要循环遍历无法提高性能。于是考虑了下之前的优化手段–本地缓存方式服务启动之后,开启一个带定时执行的线程池,对t_employ_record_bill表中的所有数据的审批历史都通过定时任务先初始化一下,然后转换生成List<ApproveHistoryBean>对象,JSON化之后存入本地。
启动服务测试,314条数据导出时间如下:
第二版导出实践.jpg
第三版实现:
由于数据加载是加载所有数据,包括员工(在职,离职分两个接口),部门数据,加载也是通过远程加载的,同时这个报表理论上不会对员工数据的实时性有要求,所以可以采用和本地缓存审批历史记录一样的策略,分别生成List<StaffCacheBean>对象存所有在职离职员工数据,属性大概6个,数据量大概3w,List<DepartmentCacheBean>存所有部门数据,属性大概有5个,数据量不到2000.
启动服务测试,314条数据导出时间如下:
第三版导出实践.jpg
可以看到现在整体导出全部测试环境数据314条记录平均用时不到1s,实现超10倍性能优化成果。
当然还有其他优化细节:
1.由于数据定时获取过程中在定时任务服务里还是有循环调用,为了避免服务方出现问题,每100条休息20ms。
2.由于存在数据缓存失效的可能,程序里也加了兼容代码,虽然导出性能会下降但是不会报错。
3.导出过程中难免有新流程审批历史没有被缓存到,这里同样在绘制Excel的过程中加了校验,如果本地缓存里没有则调用远程服务重新构建对应审批单据的审批记录列表进行绘制。
总体来说是四点
1.是全部数据不适合缓存远程(如Redis),调用远程服务也无法解决问题
2.报表对数据源实时性要求不高
3.空间换时间
4.缓存失效之后,如何保证服务可用。
导出过程查看GC情况,YGC正常,FGC正常。
网友评论