注意:如果难以接受sleep这种写法,可以使用延时队列进行替代。
先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力,也就是缓存穿透的问题。针对缓存穿透问题,可以用缓存空结果、布隆过滤器进行解决。
(2) 先更新数据库,再删除缓存
如果线程A更新了数据库中的值,但还没来得及删除缓存值,线程B就开始读取数据了,那么此时,线程B查询缓存时,发现缓存命中,就会直接从缓存中读取旧值。其本质也是,本应后发生的“B线程-读请求”先于“A线程-删除缓存”执行并返回了。
或者,在“先更新数据库,再删除缓存”方案下,“读写分离 主从库延迟”也会导致不一致:
解决方案:
a.延迟消息
凭借经验发送「延迟消息」到队列中,延迟删除缓存,同时也要控制主从库延迟,尽可能降低不一致发生的概率。
b.订阅binlog,异步删除
通过数据库的binlog来异步淘汰key,利用工具(canal)将binlog日志采集发送到MQ中,然后通过ACK机制确认处理删除缓存。
c.删除消息写入数据库
通过比对数据库中的数据,进行删除确认 先更新数据库再删除缓存,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力,也就是缓存穿透的问题。针对缓存穿透问题,可以用缓存空结果、布隆过滤器进行解决。
d.加锁
更新数据时,加写锁;查询数据时,加读锁。
建议:
优先使用“先更新数据库再删除缓存”的执行时序,原因主要有两个:
先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力。
业务应用中读取数据库和写缓存的时间有时不好估算,进而导致延迟双删中的sleep时间不好设置。
读写缓存:增删改在缓存中进行,并采取相应的回写策略,同步数据到数据库中。
同步直写:使用事务,保证缓存和数据更新的原子性,并进行失败重试(如果Redis本身出现故障,会降低服务的性能和可用性)
异步回写:写缓存时不同步写数据库,等到数据从缓存中淘汰时,再写回数据库(没写回数据库前,缓存发生故障,会造成数据丢失)
该策略在秒*场中有见到过,业务层直接对缓存中的秒*商品库存信息进行操作,一段时间后再回写数据库。
一致性:同步直写>异步回写,因此,对于读写缓存,要保持数据强一致性的主要思路是:利用同步直写,同步直写也存在两个操作的时序问题:更新数据库和更新缓存。
无并发情况