如何保证缓存(Redis)与数据库(MySQL)的一致性
2023-12-15 21:58:14 9 举报
AI智能生成
如何保证缓存(Redis)与数据库(MySQL)的一致性
作者其他创作
大纲/内容
对于热点数据(经常被查询,但不经常被修改的数据),我们可以将其放入redis缓存中,以增加查询效率,但需要保证从redis中读取的数据与数据库中存储的数据最终是一致的。
说明
直接从缓存返回数据
Cache Hit
从数据库将数据读入缓存
Cache Miss
读(SELECT)
不存在缓存与数据库数据不一致
执行顺序:先更新缓存还是先更新数据库?
更新缓存策略:当缓存中的内容变化时,是选择修改缓存(Update),还是直接淘汰缓存(Delete)?
写(DML)
先更新缓存,再更新数据库
先更新数据库,再更新缓存
先淘汰缓存,再更新数据库
先更新数据库,再淘汰缓存
四种方案
客户端对数据库中的数据主要有两类操作
前言
优点:操作简单,无论更新操作是否复杂,直接将缓存中的旧值淘汰
缺点:淘汰Cache后,下一次查询无法在Cache中查到,会有一次Cache Miss,这时需要重新读取数据库
淘汰Cache
优点:命中率高,直接更新缓存,不会有Cache Miss的情况
缺点:更新Cache消耗较大
更新Cache
线程A更新了缓存线程B更新了缓存线程B更新了数据库线程A更新了数据库
线程A更新了数据库 线程B更新了数据库 线程B更新了缓存 线程A更新了缓存
并发较大时
都会导致数据不一致,解决思路是“串行化”
淘汰Cache还是更新Cache?
结论:更新Cache的消耗更大,且很有可能造成数据的不一致,所以推荐直接淘汰Cache
再次查询缓存,最多会有一次Cache Miss
第一步淘汰缓存成功第二步更新数据库失败
数据库中是新数据,缓存中是旧数据,即数据不一致
第一步更新数据库成功第二步淘汰缓存失败
情景一:更新数据库与淘汰缓存需要先后执行,如果在执行过程中后一步执行失败,哪种方案对业务的影响最小?
都需要针对第二步做重试,无优劣之分
A线程进行写操作,先淘汰缓存,再更新数据库
B线程进行读操作,发现缓存中没有想要的数据,从数据库中读取更新后的新数据
正常情况
没有问题
A线程进行写操作,先成功淘汰缓存,但由于网络或其它原因,还未更新数据库或正在更新
此时是没有问题的,因为数据库中的数据还未完成更新,所以数据库与缓存此时存储的都是旧值,数据没有不一致
B线程进行读操作,发现缓存中没有想要的数据,从数据库中读取数据,但此时A线程还未完成更新操作,所以读取到的是旧数据,并且B线程将旧数据放入缓存。
此时是有问题的,数据库中是更新后的新数据,缓存中是更新前的旧数据,数据不一致。如果在缓存中没有对该值设置过期时间,旧数据将一直保存在缓存中,数据将一直不一致,直到之后再次对该值进行修改时才会在缓存中淘汰该值
在B线程将旧数据读入缓存后,A线程终于将数据更新完成
并发量较大的情况
保证对同一个数据的读写严格按照先后顺序串行化进行,避免并发较大的情况下,多个线程同时对同一数据进行操作时带来的数据不一致性。
串行化的思路
B线程进行读操作,从数据库中读入旧数据,共耗时N秒
B线程将旧数据读入缓存后,A线程将数据更新完成,此时数据不一致
A线程将数据库更新完成后,休眠M秒(M比N稍大即可),然后再次淘汰缓存,此时缓存中即使有旧数据也会被淘汰,此时可以保证数据的一致性
延时双删+设置缓存的超时时间
用“异步淘汰”的策略,将休眠M秒以及二次淘汰放在另一个线程中,A线程在更新完数据库后,可以直接返回成功而不用等待。
A线程需要在更新数据库后,还要休眠M秒再次淘汰缓存,等所有操作都执行完,这一个更新操作才真正完成,降低了更新操作的吞吐量
用“重试机制”,即当二次淘汰失败后,报错并继续重试,直到执行成功
如果第二次缓存淘汰失败,则不一致依旧会存在
新问题
优化策略
会导致cache与数据库的数据一直或很长时间不一致
B线程进行读操作,发现缓存中没有想要的数据,从数据库中读取数据,但B线程只是从数据库中读取想要的数据,并不将这个数据放入缓存中,所以并不会导致缓存与数据库的不一致
A线程更新数据库后,通过订阅binlog来异步更新缓存
并发量较大的情况下,采用异步更新缓存的策略
数据库与缓存的内容将一直都是一致的
A线程进行写操作,更新数据库,淘汰缓存
B线程进行读操作,从数据库中读取新的数据
A线程进行写操作,更新数据库,还未淘汰缓存
B线程从缓存中可以读取到旧数据,此时数据不一致
A线程完成淘汰缓存操作
其它线程进行读操作,从数据库中读入最新数据,此时数据一致
情形1
数据不一致的时间很短,数据最终是一致的
A线程进行写操作,更新数据库,但更新较慢,缓存也未淘汰
B线程进行读操作,读取了缓存中的旧数据
情形2
数据库与缓存中都是旧数据,没有数据不一致
A线程进行读操作,缓存中没有相应的数据,将从数据库中读数据到缓存
B线程执行写操作,更新数据库,淘汰缓存
B线程写操作完成后,A线程才将数据库的数据读入缓存
情形3
没有问题,A线程读取的是B线程修改后的新数据
A线程还未读取数据库的数据
可以用“延迟双删”策略,在A线程读操作完成后再淘汰一次缓存
发生的概率极低,因为一般读操作要比写操作要更快
A线程读取的是旧数据,此时数据会不一致
A线程已读取数据库的数据,不过由于网络等问题数据还未传输到缓存
分2种情况
并发较大的情况
情景二:假设没有操作会执行失败,但执行前一个操作后无法立即完成下一个操作,在并发较大的情况下,可能会导致数据不一致。此时,哪种方案对业务的影响最小?
执行顺序问题:先淘汰缓存还是先更新数据库?数据库是单节点
正文
采用同步更新缓存的策略,可能会导致数据长时间不一致,如果用延迟双删来优化,还需要考虑究竟需要延时多长时间的问题——读的效率较高,但数据的一致性需要靠其它手段来保证
采用异步更新缓存的策略,不会导致数据不一致但在数据库更新完成之前,都需要到数据库层面去读取数据,读的效率不太好——保证了数据的一致性,适用于对一致性要求高的业务
先淘汰Cache,再更新数据库
无论是同步/异步更新缓存,都不会导致数据的最终不一致,在更新数据库期间,Cache中的旧数据会被读取,可能会有一段时间的数据不一致,但读的效率很好——保证了数据读取的效率,如果业务对一致性要求不是很高,这种方案最合适
先更新数据库,再淘汰Cache
单节点下两种方案对比
结语
如何保证缓存(Redis)与数据库(MySQL)的一致性
0 条评论
回复 删除
下一页