项目场景:
优化:利用redis优化:
项目启动时,需要初始化相关角色的数据权限资源信息到内存中,避免重复DB查询,利用Redis优化查询速度。
问题描述
初始化使用Spring Data Redis,直接操作缓存数据到Redis服务端,由于数据量较大,内存和CPU压力抖动严重。进一步优化。
// 案例代码
final StopWatch stopWatch = new StopWatch ();
stopWatch.start ();
final int size = roleData.size ();
log.info ("【数据权限上下文初始化】 {} 开始", size);
if (CollUtil.isNotEmpty (roleData)) {
for (RoleDataScopeDTO roleDatum : roleData) {
Optional.ofNullable (roleDatum).ifPresent (dto -> {
final String key = dto.getRoleId ();
final Set <DataScopeDTO> values = dto.getDataScopes ();
log.info ("【初始化任务】 Role {}, RoleDatum: {} 条", key, values.size ());
redisService.setCacheSet (key, values);
});
}
}
log.info ("【数据权限上下文初始化】 {} 结束", size);
stopWatch.stop ();
log.info ("【数据权限上下文初始化】耗时 :{} /s", stopWatch.getTotalTimeSeconds ());
执行结果: 直接卡顿,编辑器卡顿,CPU和内存飙升。。。
原因分析:
CPU和内存的消耗比较大,对资源利用不太友好,系统稳定性有影响。
也会导致连接耗时较长,容易触发最长连接时间
,导致通道断开。
解决方案:
- 多线程拆分大数据量,并发插入。
- 使用Pipelined通道,批量插入,减少网络I/O。
方案一: 多线程失败,代码不写了。。
方案二: 踩坑一路,成功。
final StopWatch stopWatch = new StopWatch ();
stopWatch.start ();
redisService = Optionals.ofNullableOrDefault (RoleDataScopeContextHolder.redisService, SpringTools.getBean (RedisService.class));
RedisTemplate redisTemplate = redisService.getStringRedisTemplate ();
// 提前获取序列化方式,注意序列化和反序列化要匹配哦
final RedisSerializer keySerializer = redisTemplate.getKeySerializer ();
final RedisSerializer valueSerializer = redisTemplate.getValueSerializer ();
// 开启通道
redisTemplate.executePipelined ((RedisCallback <String>) connection -> {
final int size = roleData.size ();
log.info ("【数据权限上下文初始化】 {} 个开始", size);
if (CollUtil.isNotEmpty (roleData)) {
for (RoleDataScopeDTO roleDatum : roleData) {
Set <RoleDataScopeDTO.DataScopeDTO> dataScopes = roleDatum.getDataScopes ();
log.info ("【初始化任务】 Role {}, Ids: {} 条", roleDatum.getRoleId (), dataScopes.size ());
byte[] key = keySerializer.serialize (CacheConstant.ROLE_DATA_SCOPE + roleDatum.getRoleId ());
LinkedList <byte[]> values = new LinkedList <> ();
for (RoleDataScopeDTO.DataScopeDTO dataScope : dataScopes) {
values.add (valueSerializer.serialize (JSONUtil.toJsonStr (dataScope)));
}
// 注意:放在循环外部
connection.rPush (key, values.toArray (new byte[dataScopes.size ()][]));
}
}
log.info ("【数据权限上下文初始化】 {} 个结束", size);
return null;
});
stopWatch.stop ();
log.info ("【数据权限上下文初始化】耗时 :{} /s", stopWatch.getTotalTimeSeconds ());
优化后: 10W/S插入速度,根据本人业务场景测试得到,可做参开依据。