数据库与缓存的一致性
在更新数据库后,希望缓存也能读到正确的数据
Why?
常见的错误:
-
并发更新导致数据库与缓存不一致,两个并发写数据库:
- 先更新缓存再更新数据库、先更新数据库再更新缓存都会有并发问题,导致不一致
-
更新数据库后更新缓存失败
- 旧数据一直霸榜,过期时间兜底
延迟双删
-
读策略:发现缓存中没数据再从数据库中读取数据,更新到缓存中。
-
写策略:更新数据库中的数据;删除缓存中的数据。
-
先删后写:读写并发数据不一致
- 解决办法:延迟双删:删除缓存,更新数据库,过一会再删除一遍缓存
-
先写后删:缓存的写入通常要远远快于数据库的写入,不一致概率低
- 后删失败,数据不一致,过期时间兜底
- 分布式锁避免并发问题,性能--
-
如何保证先写后删两个操作都成功?
-
重试机制
- 第一次删除失败,将key加入消息队列,消费者复制再次删除
-
订阅 MySQL binlog,再操作缓存。
- Canal:模拟 MySQL 主从复制的交互协议,把自己伪装成一个 MySQL 的从节点,向 MySQL 主节点发送 dump 请求,MySQL 收到请求后,就会开始推送 Binlog 给 Canal,Canal 解析 Binlog 字节流之后,转换为便于读取的结构化数据,供下游程序订阅使用。
Read/Write Through(读穿 / 写穿)策略
应用程序只和缓存交互,不再和数据库交互,缓存和数据库交互,更新数据库的操作由缓存代理。
-
Read Through 策略
- 先查询缓存中数据是否存在,如果存在则直接返回,如果不存在,则由缓存组件负责从数据库查询数据,并将结果写入到缓存组件,最后缓存组件将数据返回给应用。
-
Write Through 策略
-
当有数据更新的时候,先查询要写入的数据在缓存中是否已经存在:
- 已经存在:更新缓存中的数据,同步更新到数据库中,告知应用程序更新完成。
- 不存在:直接更新数据库,然后返回;
-
Memcached、Redis 都支持。在使用本地缓存的时候可以考虑使用这种策略。
分布式锁
- 读:发现缓存不存在,读数据库前加分布式锁
- 写:加分布式锁再写数据库,写完释放锁+删缓存
分布式锁的实现:😡😡😡
Redis nx + double check