缓存实践
更新缓存的四种设计模式
Cache Aside Pattern
- 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
- 命中:应用程序从cache中取数据,取到后返回。
- 更新:先把数据存到数据库中,成功后,再让缓存失效。
缺陷:并发查询(没命中缓存在SQL中查到老数据)与更新会导致缓存存在脏数据
Read/Write Through Pattern
- Read Through:在查询操作中更新缓存
- Write Through:在更新操作的时候更新缓存,再去更新数据
缺陷:如上
Write Behind Caching Pattern
在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。
缺陷:数据不是强一致性,系统不可控性
Java系统中缓存与事务带来的问题
- Spring Transaction与Spring Cache使用时,当外层事务回滚,这时候缓存中的数据可能已经更新,是回滚之前的数据。
- 可能外层事务还没有提交, 其他线程缓存就把刷新了,缓存中会存在脏数据
两种分布式缓存更新的方案
基于阿里云DTS进行缓存刷新
- 缓存更新:对目标数据查询的时候,没有命中缓存则对数据进行缓存,存储到Redis中
- 缓存删除:利用阿里云数据传输DTS服务数据库订阅监控,监控数据库目标数据表,对进行UPDATE操作的数据进行缓存删除。
- 规避风险:利用DTS服务,隔离事务与缓存,确保数据入库后再对数据进行缓存删除,确保缓存更新的时候拿到的是最新的数据。
- 避免DTS单点故障:dts消费实例只能是一个进程,不能像mq一样分布式消费. 这样在重启dts消费进程的时候,dts消息的处理逻辑就有短暂的时间不可用。 优化:新建立dts消费者进程, 处理dts消息, 并将消息转化为mq消息。
基于Spring Transaction后置处理机制
- 关于TransactionSynchronizationAdapter
public abstract class TransactionSynchronizationAdapter implements TransactionSynchronization, Ordered {
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public void suspend() {
}
@Override
public void resume() {
}
@Override
public void flush() {
}
@Override
public void beforeCommit(boolean readOnly) {
}
@Override
public void beforeCompletion() {
}
@Override
public void afterCommit() {
}
//完成事务后进行回调操作
@Override
public void afterCompletion(int status) {
}
}
- 缓存更新:对目标数据查询的时候,没有命中缓存则对数据进行缓存,存储到Redis中
- 缓存删除:创建AfterTransactionService实现TransactionSynchronizationAdapter,对完成后的事务进行缓存清除操作