目录
一、概述
缓存优化是一个性价比很高的优化手段,多数情况下,缓存优化可以通过一些简单的操作,换来性能的大幅提升。缓存优化的核心思想就是将一些原本保存在磁盘(例如MySQL)中的、经常访问并且查询开销比较大的数据,临时保存到内存(例如Redis)中。后序再访问相同数据时,就可直接从内存中获取结果,而无需再访问磁盘,由于内存的读写速度远高于磁盘,因此就能极大的提高程序的性能。

在使用缓存优化时,有一个问题不得不提,那就是数据库和缓存数据的一致性,当数据库中的数据发生变化时,缓存中的数据也要同步更新,否则就会出现数据不一致的问题,解决该问题的方案有如下几个
-
数据发生变化时,更新数据库的同时也更新缓存
-
数据发生变化时,更新数据库的同时删除缓存
在了解了缓存优化的核心思想后,我们以移动端中的根据ID获取房间详情接口为例,进行缓存优化。该接口涉及多表查询,查询时会多次访问数据库,查询代价较高,故可采取缓存优化,加快查询速度。
二、编写缓存逻辑
1.自定义RedisTemplate
由于之前的StringRedisTemplate<String,String>和RedisTemplate<Object,Object>,不符合我们的需求。所以我们需要自己定义。
@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;
}
}
2.编写缓存逻辑
首先先理清楚缓存逻辑:第一步要先根据key在redis数据库里查询是否存在RoomDetailVo类型的value,如果查询的value为null,则把查询到的数据组装到RoomDetailVo对象中,否则直接返回该对象。
public RoomDetailVo getDetailById(Long id) {
//创建一个key
String key= RedisConstant.APP_ROOM_PREFIX+id;
//根据key在redis缓存里查询value
RoomDetailVo roomDetailVo = (RoomDetailVo)redisTemplate.opsForValue().get(key);
/**
* 如果redis中查询到的value为空,则把数据库查询到的对象缓存到redis中
*/
if(roomDetailVo==null){
//1.查询房间信息
RoomInfo roomInfo = roomInfoMapper.selectById(id);
if (roomInfo == null) {
return null;
}
//2.查询图片
List<GraphVo> graphVoList = graphInfoMapper.selectListByItemTypeAndId(ItemType.ROOM, id);
//3.查询租期
List<LeaseTerm> leaseTermList = leaseTermMapper.selectListByRoomId(id);
//4.查询配套
List<FacilityInfo> facilityInfoList = facilityInfoMapper.selectListByRoomId(id);
//5.查询标签
List<LabelInfo> labelInfoList = labelInfoMapper.selectListByRoomId(id);
//6.查询支付方式
List<PaymentType> paymentTypeList = paymentTypeMapper.selectListByRoomId(id);
//7.查询基本属性
List<AttrValueVo> attrValueVoList = attrValueMapper.selectListByRoomId(id);
//8.查询杂费信息
List<FeeValueVo> feeValueVoList = feeValueMapper.selectListByApartmentId(roomInfo.getApartmentId());
//9.查询公寓信息
ApartmentItemVo apartmentItemVo = apartmentInfoService.selectApartmentItemVoById(roomInfo.getApartmentId());
roomDetailVo = new RoomDetailVo();
BeanUtils.copyProperties(roomInfo, roomDetailVo);
roomDetailVo.setApartmentItemVo(apartmentItemVo);
roomDetailVo.setGraphVoList(graphVoList);
roomDetailVo.setAttrValueVoList(attrValueVoList);
roomDetailVo.setFacilityInfoList(facilityInfoList);
roomDetailVo.setLabelInfoList(labelInfoList);
roomDetailVo.setPaymentTypeList(paymentTypeList);
roomDetailVo.setFeeValueVoList(feeValueVoList);
roomDetailVo.setLeaseTermList(leaseTermList);
System.out.println("获取房间详情-"+Thread.currentThread().getName());
redisTemplate.opsForValue().set(key,roomDetailVo);
}
//保存浏览历史
browsingHistoryService.saveHistory(LoginUserHolder.getLoginUser().getUserId(),id);
return roomDetailVo;
}
3.编写删除缓存逻辑
要明白一个逻辑就是:如果进行了更新或删除操作,但是也能在redis中查询到更新前的value,那么最后根据id查询房间信息,是得到更新前还是更新后呢?
答案很显然:是更新前。因为更新了MySQL数据库的内容,但是redis里的内容并没有发生改变,当调用查询接口的时候,返回的是redis查询出来的对象。这就是缓存数据的一致性问题。
因此,当进行更新或删除操作时,我们还要清空缓存。
public void saveOrUpdateRoom(RoomSubmitVo roomSubmitVo) {
boolean isUpdate=roomSubmitVo.getId()!=null;
super.saveOrUpdate(roomSubmitVo);
//当id不为空时,更新,需要先删除所有信息再新增保存
if (isUpdate){
//1.删除租期关系
LambdaQueryWrapper<RoomLeaseTerm> roomLeaseTermQueryWrapper = new LambdaQueryWrapper<>();
roomLeaseTermQueryWrapper.eq(RoomLeaseTerm::getRoomId,roomSubmitVo.getId());
roomLeaseTermService.remove(roomLeaseTermQueryWrapper);
//2.删除支付方式
LambdaQueryWrapper<RoomPaymentType> roomPaymentTypeQueryWrapper = new LambdaQueryWrapper<>();
roomPaymentTypeQueryWrapper.eq(RoomPaymentType::getRoomId,roomSubmitVo.getId());
roomPaymentTypeService.remove(roomPaymentTypeQueryWrapper);
//3.删除属性值
LambdaQueryWrapper<RoomAttrValue> roomAttrValueLambdaQueryWrapper = new LambdaQueryWrapper<>();
roomAttrValueLambdaQueryWrapper.eq(RoomAttrValue::getRoomId,roomSubmitVo.getId());
roomAttrValueService.remove(roomAttrValueLambdaQueryWrapper);
//4.删除标签
LambdaQueryWrapper<RoomLabel> roomLabelLambdaQueryWrapper = new LambdaQueryWrapper<>();
roomLabelLambdaQueryWrapper.eq(RoomLabel::getRoomId,roomSubmitVo.getId());
roomLabelService.remove(roomLabelLambdaQueryWrapper);
//5.删除配套
LambdaQueryWrapper<RoomFacility> roomFacilityLambdaQueryWrapper = new LambdaQueryWrapper<>();
roomFacilityLambdaQueryWrapper.eq(RoomFacility::getRoomId,roomSubmitVo.getId());
roomFacilityService.remove(roomFacilityLambdaQueryWrapper);
//6.删除图片
LambdaQueryWrapper<GraphInfo> graphQueryWrapper = new LambdaQueryWrapper<>();
graphQueryWrapper.eq(GraphInfo::getItemType, ItemType.ROOM);
graphInfoService.remove(graphQueryWrapper);
//删除缓存
String key= RedisConstant.APP_ROOM_PREFIX+roomSubmitVo.getId();
redisTemplate.delete(key);
}
//新增保存
//1.保存图片信息
List<GraphVo> graphVoList = roomSubmitVo.getGraphVoList();
if (!CollectionUtils.isEmpty(graphVoList)){
ArrayList<GraphInfo> graphInfos = new ArrayList<>();
for (GraphVo graphVo : graphVoList) {
GraphInfo graphInfo = new GraphInfo();
graphInfo.setUrl(graphVo.getUrl());
graphInfo.setItemId(roomSubmitVo.getId());
graphInfo.setItemType(ItemType.ROOM);
graphInfo.setName(graphVo.getName());
graphInfos.add(graphInfo);
}
graphInfoService.saveBatch(graphInfos);
}
public void removeRoomById(Long id) {
//1.删除房间信息
super.removeById(id);
//2.删除房间租期关系
LambdaQueryWrapper<RoomLeaseTerm> roomLeaseTermQueryWrapper = new LambdaQueryWrapper<>();
roomLeaseTermQueryWrapper.eq(RoomLeaseTerm::getRoomId,id);
roomLeaseTermService.remove(roomLeaseTermQueryWrapper);
//3.删除支付方式关系
LambdaQueryWrapper<RoomPaymentType> roomPaymentTypeQueryWrapper = new LambdaQueryWrapper<>();
roomPaymentTypeQueryWrapper.eq(RoomPaymentType::getRoomId,id);
roomPaymentTypeService.remove(roomPaymentTypeQueryWrapper);
//4.删除属性值关系
LambdaQueryWrapper<RoomAttrValue> roomAttrValueQueryWrapper = new LambdaQueryWrapper<>();
roomAttrValueQueryWrapper.eq(RoomAttrValue::getRoomId,id);
roomAttrValueService.remove(roomAttrValueQueryWrapper);
//5.删除标签关系
LambdaQueryWrapper<RoomLabel> roomLabelLambdaQueryWrapper = new LambdaQueryWrapper<>();
roomLabelLambdaQueryWrapper.eq(RoomLabel::getRoomId,id);
roomLabelService.remove(roomLabelLambdaQueryWrapper);
//6.删除配套关系
LambdaQueryWrapper<RoomFacility> roomFacilityLambdaQueryWrapper = new LambdaQueryWrapper<>();
roomFacilityLambdaQueryWrapper.eq(RoomFacility::getRoomId,id);
roomFacilityService.remove(roomFacilityLambdaQueryWrapper);
//7.删除图片信息
LambdaQueryWrapper<GraphInfo> graphQueryWrapper = new LambdaQueryWrapper<>();
graphQueryWrapper.eq(GraphInfo::getItemType,ItemType.ROOM);
graphQueryWrapper.eq(GraphInfo::getItemId,id);
graphInfoService.remove(graphQueryWrapper);
//删除缓存
String key=RedisConstant.APP_ROOM_PREFIX+id;
redisTemplate.delete(key);
}
三、压力测试
结果很显然,有缓存的响应时间比无缓存的响应时间要少。

806

被折叠的 条评论
为什么被折叠?



