首页IT科技系统优化手机排行(不扒瞎,这个程序让我从300s优化到了10s)

系统优化手机排行(不扒瞎,这个程序让我从300s优化到了10s)

时间2025-07-31 15:38:43分类IT科技浏览5408
导读:前天晚上加班完成部门Q4KPI考核计划后,看到业务开发组的几个小伙伴在处理生产问题。我上前了解情况。...

前天晚上加班完成部门Q4KPI考核计划后             ,看到业务开发组的几个小伙伴在处理生产问题             。我上前了解情况                   。

销管系统                   ,客户交易明细页面        ,查询客户交易数据的逻辑是:调用远程数据中心接口          ,拿到原始交易数据集合                  ,然后在内存里通过相关id来给客户名称             、服务商名称                     、销售人员名称      、所属部门         、上级销售主管赋值        。

产品经理反馈           ,销售主管登陆系统查询数据时       ,非常慢                  ,慢到4~5分钟          。

查看日志              ,发现后台程序处理耗时动辄高达300s                  。

300s是个庞大的数字!当务之急    ,是看能不能降低到10s以内           。通过分析                   ,其中                 ,获取远程交易数据耗时≈6s,本地内存数据匹配竟然耗时200多秒                ,incredible!unbelievable!

那接下来要对各个匹配数据的程序段来分析       。通过细化耗时                    ,发现在for循环匹配销售数据为销售人员名称                     、所属部门         、上级销售主管赋值时    ,异常地慢                  。

贴出来这段代码:

其中             ,CacheUtil封装了Redis的get/set操作              。

emaxSalerMapper#selectSaleDepartRelation是查数据库获取基础关系数据                   ,共223条数据        ,耗时6~7ms    。

CommonRequestDTO是一个pojo模型类                   。

那么          ,这段代码也看不出哪里慢呀!

仔细一分析                  ,发现端倪                 。Cc同学怀疑问题出在读redis上。果不其然           ,for循环里频繁调用redis获取集合数据       ,尤其是当查询数据记录多循环次数多时                  ,必然拉跨                。

当务之急              ,最好的解决办法    ,是用本地缓存来搞                   ,HutoolCache登场                    。
staticTimedCache<String ,List<CommonRequestDTO>> cache= cn.hutool.cache.CacheUtil.newTimedCache(SaleCommonConstant.EXPIRY_SECONDS);
/**
* 查询销售与部门的关联关系
* @param saleId
* @return
*/
publicCommonRequestDTO selectSaleDepartRelation(Integer saleId){
if(cache.get(SaleCommonConstant.SALE_DEPART_RELATION)==null){
cache.put(SaleCommonConstant.SALE_DEPART_RELATION, emaxSalerMapper.selectSaleDepartRelation());
}
List<CommonRequestDTO> relationList = cache.get(SaleCommonConstant.SALE_DEPART_RELATION);
relationList = relationList.stream().filter(o -> saleId.equals(o.getSaleId())).collect(Collectors.toList());
if(CollectionUtils.isNotEmpty(relationList)){
CommonRequestDTO commonRequestDTO = relationList.get(0);
commonRequestDTO.setSaleName(commonRequestDTO.getSaleName());
commonRequestDTO.setDepartName(commonRequestDTO.getDepartName());
commonRequestDTO.setDepartHeaderName(commonRequestDTO.getDepartHeaderName());
returncommonRequestDTO;
}
returnnull;
}

改造完成                 ,再测试,发现这段代码耗时已经到ms级了    。整体方法耗时也控制在了10s以内             。

那么                ,回过头来分析                    ,我们看程序里redis-RedisTemplate配置    ,valueSerializer使用Jackson2JsonRedisSerializer             ,Jackson2JsonRedisSerializer序列化使用ObjectMapper                   。

/**
* RedisTemplate配置
* @param lettuceConnectionFactory
* @return
*/
@Bean
publicRedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
// 设置序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =newJackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om =newObjectMapper();
om.setVisibility(PropertyAccessor.ALL, Visibility.ANY);
om.enableDefaultTyping(DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate =newRedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
RedisSerializer<?> stringSerializer =newStringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);// key序列化
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化
redisTemplate.afterPropertiesSet();
returnredisTemplate;
}

ObjectMapper在序列化时                   ,会将所有的字段序列化        ,无论这些字段是否有值(是否为null)        。再看CommonRequestDTO          ,有多达22个属性                  ,可见在本案List<CommonRequestDTO>中有223个元素时           ,数据体积无形中增大很多          。通过下面对ObjectMapper的测试代码来比较一下       ,很明显可以看到单个对象序列化后在数据量方面的差异:

@Test
publicvoidtestObjectMapper2()throwsJsonProcessingException {
ObjectMapper om =newObjectMapper();
om.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.PUBLIC_ONLY)
.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
System.out.println("序列化所有字段(无论这些字段是否有值) ↓ ↓ ↓");
System.out.println(newString(om.writeValueAsBytes(newCommonRequestDTO())));
om =newObjectMapper();
om.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.PUBLIC_ONLY)
.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL)
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
System.out.println("不序列化空值字段 ↓ ↓ ↓");
System.out.println(newString(om.writeValueAsBytes(newCommonRequestDTO())));
}
序列化所有字段(无论这些字段是否有值) ↓ ↓ ↓
["com.emax.memberaccount.restapi.vo.CommonRequestDTO",{"enterpriseId":null,"enterpriseBizId":null,"enterpriseName":null,"saleId":null,"product":null,"entStatus":null,"departId":null,"agentId":null,"levyId":null,"departHeaderId":null,"saleIds":null,"enterpriseIds":null,"productList":null,"createTimeBegin":null,"createTimeEnd":null,"saleName":null,"departName":null,"departHeaderName":null,"ifDepartHeader":null,"loginSalerId":null,"selectEnterpriseId":null,"orderEndTime":null,"enterpriseProductDTOS":null}]
不序列化空值字段↓ ↓ ↓
["com.emax.memberaccount.restapi.vo.CommonRequestDTO",{}]

因此                  ,我们的程序有必要加上这个控制              ,即只序列化非空字段                  。

另外    ,就像我之前经常提到的                   ,会 is one thing                 ,会用 is another           。本案也再一次敲响了警钟:在使用redis分布式缓存时,尤其控制缓存大对象                ,更要严禁高频访问大对象缓存       。
声明:本站所有文章                    ,如无特殊说明或标注    ,均为本站原创发布                  。任何个人或组织             ,在未征得本站同意时                   ,禁止复制      、盗用                     、采集            、发布本站内容到任何网站   、书籍等各类媒体平台              。如若本站内容侵犯了原著者的合法权益        ,可联系我们进行处理    。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
影响体重的因素(从饮食、运动、睡眠到心理层面,多方位了解你的体重问题) 如何设计一个的网站提高SEO效果(从用户体验、内容质量和技术优化三方面入手)