概述
缓存优化是一个性价比很高的优化手段,多数情况下,缓存优化可以通过一些简单的操作,换来性能的大幅提升。缓存优化的核心思想就是将一些原本保存在磁盘(例如MySQL)中的、经常访问并且查询开销比较大的数据,临时保存到内存(例如Redis)中。后序再访问相同数据时,就可直接从内存中获取结果,而无需再访问磁盘,由于内存的读写速度远高于磁盘,因此就能极大的提高程序的性能。
在使用缓存优化时,有一个问题不得不提,那就是**数据库和缓存数据的一致性**,当数据库中的数据发生变化时,缓存中的数据也要同步更新,否则就会出现数据不一致的问题,解决该问题的方案有如下几个
- 数据发生变化时,更新数据库的同时也更新缓存
- 数据发生变化时,更新数据库的同时删除缓存
编写缓存逻辑
在了解了缓存优化的核心思想后,我们以移动端中的`根据ID获取房间详情`接口为例,进行缓存优化。该接口涉及多表查询,查询时会多次访问数据库,查询代价较高,故可采取缓存优化,加快查询速度
- 自定义RedisTemplate
- 本项目使用Reids保存缓存数据,因此我们需要使用RedisTemplate进行读写操作。
- 前文回顾
-
- Spring-data-redis提供了StringRedisTemplate和RedisTemplate<Object,Object>两个实例。
- StringRedisTemplate只能操作String类型的key和value, 我们缓存的数据是对象类型, 所以不适
- RedisTemplate可以操作Object类型的key和value, 是可以使用的
- StringRedisTemplate使用的是redis提供的string类型的序列化器, 不存在乱码问题
- RedisTemplate使用的是JDK提供的序列化器, 在java中读写不乱吗, 但是在图形客户端和命令行客户端中查看数据存在乱码的问题
- 所以这两个实例均不满足我们当前的需求,所以我们需要自定义RedisTemplate
- 在**common模块**中创建`com.atguigu.lease.common.redis.RedisConfiguration`类,内容如下
@Configuration
public class RedisConfiguration {
@Bean
public RedisTemplate<String, Object> stringObjectRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
template.setKeySerializer(RedisSerializer.string());
template.setValueSerializer(RedisSerializer.java());
return template;
}
}
- 编写缓存逻辑
修改`getDetailById`方法,如下
package com.atguigu.lease.web.app.service.impl;
/**
* @author liubo
* @description 针对表【room_info(房间信息表)】的数据库操作Service实现
* @createDate 2023-07-26 11:12:39
*/
@Service
@Slf4j
public class RoomInfoServiceImpl extends ServiceImpl<RoomInfoMapper, RoomInfo>
implements RoomInfoService {
@Autowired
private RedisTemplate redisTemplate;
@Override
public RoomDetailVo getDetailById(Long id) {
String key = RedisConstant.APP_ROOM_PREFIX + id;
RoomDetailVo roomDetailVo = (RoomDetailVo) redisTemplate.opsForValue().get(key);
if (roomDetailVo == null) {
//1.查询房间基础信息
... ...
//9.查询公寓信息
... ...
roomDetailVo = new RoomDetailVo();
... ...
redisTemplate.opsForValue().set(key, roomDetailVo);
}
// 保存浏览记录
browsingHistoryService.saveHistory(LoginUserHolder.getLoginUser().getUserId(), id);
return roomDetailVo;
}
}
- 编写删除缓存逻辑
为保证缓存数据的一致性,在房间信息发生变化时,需要删除相关缓存。
package com.atguigu.lease.web.admin.service.impl;
/**
* @author liubo
* @description 针对表【room_info(房间信息表)】的数据库操作Service实现
* @createDate 2023-07-24 15:48:00
*/
@Service
public class RoomInfoServiceImpl extends ServiceImpl<RoomInfoMapper, RoomInfo>
implements RoomInfoService {
@Autowired
private RedisTemplate redisTemplate;
@Override
public void saveOrUpdateRoom(RoomSubmitVo roomSubmitVo) {
// 1. 更新基础信息表
... ...
if (isUpdate) {
// 2. 更新操作: 先删除再添加
... ...
// 删除缓存
redisTemplate.delete(RedisConstant.APP_LOGIN_PREFIX + roomSubmitVo.getId());
}
... ...
}
@Override
public void removeRoomById(Long id) {
.... ...
// 删除缓存
redisTemplate.delete(RedisConstant.APP_LOGIN_PREFIX + id);
}
}
压力测试
可使用Postman或者Apifox等工具对`根据ID获取房间详情`这个接口进行压力测试,下图是增加缓存前后的测试报告