本文已参与「新人创作礼」活动,一起开启掘金创作之路。
缓存是如何使用的
一般而言,缓存是因为系统之间,不同的介质访问速度不一样才存在的。例如CPU中的多级缓存
,所以缓存的特性大抵都是访问快,容量小。由此,Redis根据缓存的作用可以分为两种类型:只读缓存
,读写缓存
。
只读缓存
先聊聊只读缓存,这也是缓存的日常使用方式。为了提速应用服务的查询请求、缓解MySQL等业务数据库的压力,如下图:
首先,只读缓存下有两种业务操作
更新操作:业务系统更改数据,①删除缓存里面的值,②直接操作修改数据库,③应用返回更新成功。
查询操作:业务系统查询数据,①从缓存里面查数据,②如果没有查到,则出现缓存缺失,去数据库里面查,③应用程序从数据库拿到值之后再写会Redis里,返回查询到的数据。
读写缓存
读写缓存是将缓存作为业务数据库使用,最新数据是在Redis里面的。正如你担忧的那样,Redis是内存数据,数据的安全性会由于服务的不可用而发生数据丢失,从而有着业务风险。
如图,展示了读写缓存的流程,我们可以看到业务数据的修改直接放在了缓存里,而数据的持久化更多的是根据数据的安全性、请求的响应速度来评估是同步直写
还是异步写回
。
在生产实践中,我们根据不同的场景来选择不同的缓存使用方式。
缓存不一致
之所以要先聊缓存在使用上的区别,是因为不同的使用方式,造成的缓存不一致处理方式也不一样。首先,我们先定义一下,什么是缓存一致。
缓存一致:①缓存中有数据,缓存和数据库一致,才是一致②缓存中没有,数据中是最新的数据,才是一致的。
读写缓存的一致性:如我们上面聊得缓存策略,读写缓存要保证数据一致,必须采用同步直写的方式,通过业务系统来保证Redis和MySQL的分布事务,从而是的数据的更新保证原子性。
只读缓存的一致性,情况比较复杂,可以看一看上面的只读缓存的流程。
操作顺序 | 问题 |
---|---|
先删除缓存,再更新数据库 | 如果数据库更新时间较长,再次访问,缓存缺失,从数据库读取旧值,数据不一致 |
先更新数据库,再删除缓存 | 删除缓存失败或者在改数据库值间隙,访问时候缓存命中旧值,数据不一致 |
那么我们依次进行讨论:
不一致情况一:先删缓存,再更新数据库
时间 | 线程A | 线程B | 出现状况 |
---|---|---|---|
t1 | 删除缓存 | ||
t2 | 1、读取缓存,缓存缺失,从数据库load出旧值 2、数据写入缓存 | 由于线程B的操作,其他线程可能会读到旧值 | |
t3 | 更新数据库 | 缓存和数据库不一致 |
对于此类情况下造成的数据不一致,我们可以采用延迟双删
的策略。也就是我们再更新完成之后,再删除一次。
时间 | 线程A | 线程B | 出现状况 |
---|---|---|---|
t1 | 删除缓存 | ||
t2 | 1、读取缓存,缓存缺失,从数据库load出旧值 2、数据写入缓存 | 由于线程B的操作,其他线程可能会读到旧值 | |
t3 | 更新数据库 | ||
t4 | sleep | 等待一下其他读了旧值的线程,返回 | |
t5 | 删除缓存 | 缓存中没有值,数据库中是最新值,数据一致 |
不一致情况二:先更新数据库,再删除缓存
时间 | 线程A | 线程B | 出现状况 |
---|---|---|---|
t1 | 更新数据库 | ||
t2 | 1、读取缓存,缓存命中,读到旧值 | 线程A尚删除缓存,并发请求读到旧值 | |
t3 | 删除缓存 |
在这种情况下,由于删除缓存的操作较为快速,如果再线程并发不高的情况下,对业务的影响比较小。
总结
对于缓存不一致的解决方案,思路大概是这样,总体来说,我们需要根据我们业务特性、缓存使用方式来应对不同的情况。如果你有任何问题,欢迎与我交流。