文章目录
- 神领物流 -- 如何实现多级缓存以及缓存之间数据的一致性
- 一. 为什么要使用多级缓存?
- 二. 为什么要选择MongoDB作为数据库
- 三. 如何缓存之间的一致性
- 1. 如何同步更新Redis缓存
- 2. 如何同步更新CaffeineCache缓存
神领物流 – 如何实现多级缓存以及缓存之间数据的一致性
采用用到的技术:
- MongoDB
- Redis
- CaffeineCache
一. 为什么要使用多级缓存?
在我们的项目当中 ,特别是在电商大促期间,快件数量非常庞大,也就意味着查询人的量也是很大的 , 因此读查询接口造成的并发压力也会非常大 . 因此为了处理高并发问题在这采用了多级缓存
二. 为什么要选择MongoDB作为数据库
首先对于物流的订单流转信息其主体由一个运单号作为标识以及一个列表用来保存所有的运输信息 , 比如某货物在运输过程中从A送往B , 某货物已送达B点
这里如果使用mysql作为数据源我们就不可避免的需要将所有的运输信息保存在另一张表中 , 然后通过运单号进行查询 , 这对MySQL的性能开销会非常大
因此在这里采取了MongoDB作为数据库 , MongoDB的嵌套保存天然支持我们的业务需求 , 同时MongoDB在处理高并发上其性能也会优于MySQL
三. 如何缓存之间的一致性
在项目当中采用CaffeineCache作为一级缓存Redis作为二 级缓存 , MongoDB作为数据源
CaffeineCache --> Redis --> MongoDB
基于这个模型 , 也就是说的哪个MongoDB数据库中的数据被修改 , 那么我们就要对缓存进行同步更新
1. 如何同步更新Redis缓存
/**
* 如果运单数据不存在,就创建,否则更新数据
*
* @param transportOrderId 运单id
* @param infoDetail 信息详情
* @return 运输信息数据
*/
@CachePut(value = "transport-info", key = "#p0")
@Override
public TransportInfoEntity saveOrUpdate(String transportOrderId, TransportInfoDetail infoDetail) {
TransportInfoEntity mongoTemplateOne = mongoTemplate.findOne(Query.query(Criteria.where("transportOrderId").is(transportOrderId)), TransportInfoEntity.class);
if (ObjectUtil.isEmpty(mongoTemplateOne)){
mongoTemplateOne.setId(new ObjectId());
mongoTemplateOne.setTransportOrderId(transportOrderId);
mongoTemplateOne.setInfoList(ListUtil.toList(infoDetail));
mongoTemplateOne.setUpdated(System.currentTimeMillis());
}else {
mongoTemplateOne.getInfoList().add(infoDetail);
}
//无论新增还是更新都要设置更新时间
mongoTemplateOne.setUpdated(System.currentTimeMillis());
//保存/更新到MongoDB
return this.mongoTemplate.save(mongoTemplateOne);
}
在对数据的更新同时我们使用SpringCache集成redis , 并使用其更新注解 @CachePut(value = “transport-info”, key = “#p0”)对redis数据同步更新 , 这样就解决了数据库与redis之间的缓存更新问题
2. 如何同步更新CaffeineCache缓存
对于CaffeineCache来说它是基于JVM级别的缓存 , 也就是说在搭建了集群后如果不对其同步很有可能导致不同机器上的CaffeineCache缓存数据不同
为了解决这个问题 , 我们通过使用redis内部的发布订阅来实现 , 也就是说每当Redis的数据被更新 , 我们就会向指定的key发生一个消息
其原理就是redis支持对key的订阅 , 同时可以进行监听 , 当该key发生变化时就会调用CaffeineCache删除指定运单的缓存
/**
* redis消息监听,解决Caffeine一致性的问题
*/
@Component
public class RedisMessageListener extends MessageListenerAdapter {
@Resource
private Cache<String, TransportInfoDTO> transportInfoCache;
@Override
public void onMessage(Message message, byte[] pattern) {
//获取到消息中的运单id
String transportOrderId = Convert.toStr(message);
//将本jvm中的缓存删除掉
this.transportInfoCache.invalidate(transportOrderId);
}
}