不管是先写数据库再删除缓存,还是先删除缓存再写数据库,都会出现缓存不一致情况:
- 如果先删除缓存,还没有来得及写mysql,这时候另外一个线程读取数据发现缓存为空,就读取数据库旧数据并更新缓存。此时写入mysql后缓存数据为脏数据。
- 先写数据库,再删除缓存之前写库的线程挂了没有及时更新缓存,也会出现不一致情况
严格要求缓存+数据库必须一致性
唯一方案:读请求和写请求串行化。根据唯一标识路由之后,将请求串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况
缺点:串行化后吞吐量大幅度降低
致命缺点:内存队列读写串行化只能保证单机情况,集群需要保证集群串行化。可考虑分布式锁,利用zk的临时顺序节点进行集群服务排队!!
最经典的缓存+数据库读写的模式
1、Cache Aside Pattern
(1)读的时候,先读缓存,缓存没有的话,那么就读数据库,然后取出数据后放入缓存,同时返回响应
(2)更新的时候,先删除缓存,然后再更新数据库
2、为什么是删除缓存,而不是更新缓存呢?
原因一:很简单,很多时候,复杂点的缓存的场景,因为缓存有的时候,不简单是数据库中直接取出来的值,需要计算
扫描二维码关注公众号,回复:
12445916 查看本文章

原因二:数据频繁修改,但是查询不频繁。即使每次及时更新缓存也没有人去查询
重点来了!!!!!!!!!
缓存不一致解决方案
方案一:延时双删+设置超时
操作步骤
- 先删除缓存
- 再写数据库
- 休眠500毫秒(休眠时间要看具体的业务。考虑到redis主从延迟)
- 再次删除缓存
备注:在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。这样最差的情况是在超时时间内存在不一致,当然这种情况极其少见,可能的原因就是服务宕机。此种情况可以满足绝大多数需求。 当然这种策略要考虑redis和数据库主从同步的耗时,所以在第二次删除前最好休眠一定时间,比如500毫秒,这样毫无疑问又增加了写请求的耗时
参考:https://blog.csdn.net/belongtocode/article/details/104355460
方案二:异步更新缓存(基于订阅binlog的同步机制)
优点:业务代码侵入少,可保证最终一致性