对于内存淘汰和超时剔除,可见我的另一篇博客:数据库面试题——redis中key没有设置过期时间但被redis主动删除(8种内存淘汰策略)
这篇博客主要讲主动更新,主动更新策略有三种:
- Cache Aside Pattern:由缓存的调用者在更新数据库的同时更新缓存,这种方法需编程实现,业务比较复杂
- Read/Write Through Pattern:缓存与数据库整合为一个服务,由服务来维护一致性。调用者调用该服务,无需关心缓存一致性问题。这个方法最大的问题就是维护这样一个服务比较复杂,当前市面上没有完善的解决方案
- Write Behind Caching Pattern:调用者只操作缓存,由其它线程异步的将缓存数据持久化到数据库,保证最终一致。如果异步线程还没有将最新操作持久化到数据库中redis就宕机了,会导致数据丢失
其中Cache Aside Pattern是最受欢迎的更新策略,但操作缓存和数据库时有三个问题需要考虑:
删除缓存还是更新缓存?
更新缓存:每次更新数据库都更新缓存,无效写操作较多
删除缓存:更新数据库时让缓存失效,查询时再更新缓存
因此开发中一般采用删除缓存的方式
如何保证缓存与数据库的操作的同时成功或失败?
单体系统:将缓存与数据库操作放在一个事务
分布式系统:利用TCC等分布式事务方案
先操作缓存还是先操作数据库?
先删除缓存,再操作数据库
先操作数据库,再删除缓存
可以先对这两种方案进行对比
先删除缓存,再操作数据库可能会导致下图问题,就是当线程1删除缓存后,还没有更新完数据库,线程2就来查询,发现redis没有,就去查询数据库,将脏数据写入缓存,这将导致后续所有访问都是获取redis脏数据
先操作数据库,再删除缓存可能导致下图问题,假如某数据没有缓存,线程1就去数据库读取数据,在它尚未将其写入redis的时候,线程2更新了数据库,然后在线程1还没有将旧数据写入redis的时候执行了删除缓存操作,这当然是无效删除,然后线程1再将旧数据写入redis,这也会导致后续的所有请求都拿到脏数据
可见无论是先删后写还是先写后删都有可能导致脏数据的产生,因此出现了第三种解决方案,也就是延时双删
延时双删方案执行步骤
- 删除缓存
- 更新数据库
- 延时500毫秒
- 删除缓存
为何要延时500毫秒:这是为了我们在第二次删除redis之前能完成数据库的更新操作。假象一下,如果没有第三步操作时,有很大概率,在两次删除redis操作执行完毕之后,数据库的数据还没有更新,此时若有请求访问数据,便会出现我们一开始提到的那个问题。
为何要两次删除缓存:如果我们没有第二次删除操作,此时有请求访问数据,有可能是访问的之前未做修改的redis数据,删除操作执行后,redis为空,有请求进来时,便会去访问数据库,此时数据库中的数据已是更新后的数据,保证了数据的一致性。
如果有兴趣了解更多相关内容,欢迎来我的个人网站看看:瞳孔的个人空间