Redis(3):缓存与数据库一致性
1 设计缓存
缓存利用率(内存置换策略)缓存一致性(缓存与数据库一致性)缓存过期时间(过期键策略)
2 保证缓存和数据库数据的一致性
2.1 可能的解决方案
方式 | 存在问题 | 解决方法 |
---|---|---|
先更新缓存,再更新数据库 | 更新缓存成功,更新数据库异常,导致缓存与数据库不一致 | 不考虑 |
先更新数据库,再更新缓存 | 更新数据库正常,更新缓存失败,数据不一致,缓存中数据一直存在 | |
先删除缓存,后更新数据库 | 删除缓存后,还未更新数据库,并发下另一读请求将数据读入缓存 | 延迟双删 |
先更新数据库,后删除缓存 | 1. 缓存刚好失效 2. 请求A查询数据库,得到一个旧值 3. 请求B将新值写入数据库 4. 请求B删除缓存 5. 请求A将查到的旧值写入缓存 |
异步延时双删 |
::: level1 |
先更新数据库,后删除缓存
:::
保证两个操作执行成功
1. 重试机制
引入消息队列,将第二个操作(删除缓存)要操作的数据加入到消息队列,由消费者来操作数据。
-
如果应用删除缓存失败,可以从消息队列中重新读取数据,然后再次删除缓存,这个就是重试机制。当然,如果重试超过的一定次数,还是没有成功,我们就需要向业务层发送报错信息了。
-
如果删除缓存成功,就要把数据从消息队列中移除,避免重复操作,否则就继续重试。
2. 订阅 MySQL binlog,再操作缓存。
「先更新数据库,再删缓存」的策略的第一步是更新数据库,那么更新数据库成功,就会产生一条变更日志,记录在 binlog 里。于是我们就可以通过订阅 binlog 日志,拿到具体要操作的数据,然后再执行缓存删除。
3. 缓存失效时间变短
缓存和数据库一致性问题,看这篇就够了 (qq.com)
数据库和缓存如何保证一致性? | 小林coding (xiaolincoding.com)
聊聊如何保证Redis和Mysql中数据双写一致性面试官常拷打:如何下保证MySQL数据库与Redis缓存数据一致性? (qq.com)
3 数据不一致情况
-
先删缓存,再更新数据库
-
主从同步,读写分离的情况下,读从库而产生脏数据
4 缓存读写策略
4.1 Cache Aside Pattern(旁路缓存模式)
Cache Aside(旁路缓存)策略是最常用的,应用程序直接与「数据库、缓存」交互,并负责对缓存的维护,该策略又可以细分为「读策略」和「写策略」。Cache Aside 策略适合读多写少的场景,不适合写多的场景,因为当写入比较频繁时,缓存中的数据会被频繁地清理,这样会对缓存的命中率有一些影响。
写策略的步骤:
-
先更新数据库中的数据,再删除缓存中的数据。
读策略的步骤:
-
如果读取的数据命中了缓存,则直接返回数据;
-
如果读取的数据没有命中缓存,则从数据库中读取数据,然后将数据写入到缓存,并且返回给用户。
缺陷 1:首次请求数据一定不在 cache 的问题
解决办法:可以将热点数据可以提前放入 cache 中。
缺陷 2:写操作比较频繁的话导致 cache 中的数据会被频繁被删除,这样会影响缓存命中率 。
解决办法:
-
数据库和缓存数据强一致场景:更新 db 的时候同样更新 cache,不过我们需要加一个锁/分布式锁来保证更新 cache 的时候不存在线程安全问题。
-
可以短暂地允许数据库和缓存数据不一致的场景:更新 db 的时候同样更新 cache,但是给缓存加一个比较短的过期时间,这样的话就可以保证即使数据不一致的话影响也比较小。
4.2 Read/Write Through Pattern(读写穿透)
Read/Write Through(读穿 / 写穿)策略原则是应用程序只和缓存交互,不再和数据库交互,而是由缓存和数据库交互,相当于更新数据库的操作由缓存自己代理了。
Read/Write Through Pattern 中服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中。cache 服务负责将此数据读取和写入 db,从而减轻了应用程序的职责。
1、Read Through 策略
先查询缓存中数据是否存在,如果存在则直接返回如果不存在,则由缓存组件负责从数据库查询数据,并将结果写入到缓存组件,最后缓存组件将数据返回给应用。
2、Write Through 策略
当有数据更新的时候,先查询要写入的数据在缓存中是否已经存在:
-
如果缓存中数据已经存在,则更新缓存中的数据,并且由缓存组件同步更新到数据库中,然后缓存组件告知应用程序更新完成。
-
如果缓存中数据不存在,直接更新数据库,然后返回;
4.3 Write Back(写回策略)
Write Back(写回)策略,即写入时复制(Copy On Write),在更新数据的时候,只更新缓存,同时将缓存数据设置为脏的,然后立马返回,并不会更新数据库。对于数据库的更新,会通过批量异步更新的方式进行。
实际上,Write Back(写回)策略也不能应用到我们常用的数据库和缓存的场景中,因为 Redis 并没有异步更新数据库的功能。
Write Back 是计算机体系结构中的设计,比如 CPU 的缓存、操作系统中文件系统的缓存都采用了 Write Back(写回)策略。
Write Back 策略特别适合写多的场景,因为发生写操作的时候, 只需要更新缓存,就立马返回了。比如,写文件的时候,实际上是写入到文件系统的缓存就返回了,并不会写磁盘。
4.4 Write Behind Pattern(异步缓存写入)
Write Behind Pattern 和 Read/Write Through Pattern 很相似,两者都是由 cache 服务来负责 cache 和 db 的读写。
Read/Write Through 是同步更新 cache 和 db,而 Write Behind 则是只更新缓存,不直接更新 db,而是改为异步批量的方式来更新 db。