这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天
和Mysql保持数据一致性
保底策略:设置过期
cache-aside 策略
对DB做变更操作时,不同步变更缓存,而是删除缓存中的对应条目
该策略又可以细分为「读策略」和「写策略」
-
写策略
更新数据库中的数据; 删除缓存中的数据。
-
读策略的步骤:
读取的数据命中了缓存,则直接返回数据;
如果读取的数据没有命中缓存,则从数据库中读取数据,然后回写到缓存,
最后返回数据给用户
写策略的两种顺序:
先删缓存,再更新DB
问题:执行👆两个操作中间插入一次读操作,读操作使得回写旧值到缓存,
而DB存的新值,造成不一致(删写被读加塞)
==》延时双删,删除读请求可能造成的缓存脏数据,但延迟时间不好评估
# 伪代码
def delay_delete():
redis.delKey(X) #删除缓存
db.update(X) #更新数据库
Thread.sleep(N)
redis.delKey(X) #再删除缓存
先更新DB,再删缓存
问题:对于新数据,一次读操作会有回写数据到缓存的过程,如果这之间插入👆两个操作,
会导致DB中的值被更新,同时缓存是回写的旧值,造成不一致(读被写删加塞)
==》但在实际中,DB读取+缓存回写速度 要快于 DB写入+删除缓存,所以发生概率低
第二个删除缓存 失败
会导致不一致,有两种应对方法
『异步重试』
将第二个操作(删除缓存)要操作的数据加入到消息队列,由消费者来重试,
只要失败就从消息队列中重新读取数据,直到成功
可靠性的保证:
- 消息队列保证可靠性:写到队列中的消息,成功消费之前不会丢失(重启项目也不担心)
- 消息队列保证消息成功投递:下游从队列拉取消息,成功消费后才会删除消息,否则还会继续投递消息给消费者(符合我们重试的场景)
『订阅数据库变更日志,再操作缓存』
更新数据库成功,就会产生一条变更日志,记录在 binlog 里,
通过订阅 binlog 日志,解析后,再删除缓存
==》Canal中间件,伪装成Mysql从节点,同步binlog并解析
==》思路都是 异步操作缓存