缓存实践

更新缓存的四种设计模式

Cache Aside Pattern

  1. 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  2. 命中:应用程序从cache中取数据,取到后返回。
  3. 更新:先把数据存到数据库中,成功后,再让缓存失效。

缺陷:并发查询(没命中缓存在SQL中查到老数据)与更新会导致缓存存在脏数据

Read/Write Through Pattern

  1. Read Through:在查询操作中更新缓存
  2. Write Through:在更新操作的时候更新缓存,再去更新数据

缺陷:如上

Write Behind Caching Pattern

在更新数据的时候,只更新缓存,不更新数据库,而我们的缓存会异步地批量更新数据库。

缺陷:数据不是强一致性,系统不可控性

Java系统中缓存与事务带来的问题

  1. Spring Transaction与Spring Cache使用时,当外层事务回滚,这时候缓存中的数据可能已经更新,是回滚之前的数据。
  2. 可能外层事务还没有提交, 其他线程缓存就把刷新了,缓存中会存在脏数据

两种分布式缓存更新的方案

基于阿里云DTS进行缓存刷新

  1. 缓存更新:对目标数据查询的时候,没有命中缓存则对数据进行缓存,存储到Redis中
  2. 缓存删除:利用阿里云数据传输DTS服务数据库订阅监控,监控数据库目标数据表,对进行UPDATE操作的数据进行缓存删除。
  3. 规避风险:利用DTS服务,隔离事务与缓存,确保数据入库后再对数据进行缓存删除,确保缓存更新的时候拿到的是最新的数据。
  4. 避免DTS单点故障:dts消费实例只能是一个进程,不能像mq一样分布式消费. 这样在重启dts消费进程的时候,dts消息的处理逻辑就有短暂的时间不可用。 优化:新建立dts消费者进程, 处理dts消息, 并将消息转化为mq消息。

image

基于Spring Transaction后置处理机制

  1. 关于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) {
    	}
    
}
  1. 缓存更新:对目标数据查询的时候,没有命中缓存则对数据进行缓存,存储到Redis中
  2. 缓存删除:创建AfterTransactionService实现TransactionSynchronizationAdapter,对完成后的事务进行缓存清除操作

参考

https://coolshell.cn/articles/17416.html

https://zhuanlan.zhihu.com/p/37643608