缓存与数据库更新策略

本文探讨了在数据变化时,缓存更新的四种策略:先更新缓存再更新数据库、先删除缓存再更新数据库、先更新数据库再更新缓存以及先更新数据库再删除缓存。通过分析并发场景,指出先更新数据库再删除缓存的策略(Cache-Aside pattern)在实际应用中能较好地保证数据一致性,虽然可能存在并发问题,但可以通过异步延时删除策略和消息重试机制进行优化。

缓存更新

缓存(Redis、memory cache等)被广泛应用于高并发、高性能的项目中。应用在,请求先查询缓存,命中则返回。未命中则查询数据库,并缓存。而且缓存也有过期时间的,避免浪费内存、出现不一致等,因此缓存是最终一致性的。但实际使用中,需要主动更新缓存。那就存在一个问题:为保证数据读取的正确性、一致性,是先更新缓存,还是先更新数据库?

数据变化时,缓存可以更新也可以删除,等查询的时候再次缓存即可。因此可以组合出以下四种策略:

先更新缓存,再更新数据库;
先删除缓存,再更新数据库;
先更新数据库,再更新缓存;
先更新数据库,再删除缓存;

上述策略都是两步操作完成。下边依次来分析这四种策略的可行性。

 

先更新缓存,再更新数据库

两步:第一步更新缓存,第二步更新数据库。
场景
1.线程A 尝试更新数据库,第一步更新缓存
2.线程B 尝试更新数据库,第一步更新缓存
3.线程B 第二步更新数据库
4.线程A 第二步更新数据库

此时,因为线程B 在线程A之后完成了缓存更新,但先更新了数据库。即 A-B-B-A,导致缓存中是线程B 设置的数据。在一个Ttl内,数据不一致。

 

先删除缓存,再更新数据库

场景
1.线程A 尝试更新数据库,第一步更新缓存
2.线程B 读缓存,未命中,继续读数据库,并缓存。(此时缓存的是旧数据)
3.线程A 第二步更新数据库

此时,在一个Ttl 内,缓存内的数据是旧数据。出现了数据不一致。

先更新数据库,再更新缓存

场景
1.线程A 尝试更新数据库,第一步 更新数据库
2.线程B 尝试更新数据库,第一步 更新数据库
3.线程A 第二步更新缓存
4.线程A 第二步更新缓存

此时,也是 A-B-B-A 的问题,导致缓存中是线程A 设置的数据。在一个Ttl内,数据不一致。

接下来看先更新数据库,再删除缓存。

 

先更新数据库,再删除缓存

这个也就是老外提出的《Cache-Aside pattern》

场景
1.线程A 尝试 更新数据库,第一步 更新数据库,第二步删除缓存
2.线程B 尝试 读取 缓存,未命中,读取数据库,并缓存

此时,数据一致。这个在网上的facebook的论文《Scaling Memcache at Facebook》也是选择这种策略

可能有人有疑问了:不会并发么?确实存在的:

场景
前提:缓存中无数据
1.线程A 尝试读取,未命中缓存,读数据库
2.线程B 尝试 更新数据库
3.线程B 删除缓存
4.线程A 把读取的数据更新到缓存

此时,也是 A-B-B-A 的问题。

确实,不过这里需要说明一下:这种情况发生的概率比较低!!!
我们常用的数据库mysql,包括oracle,更新操作耗时比较长,查询操作耗时比较短。如果要达到,需要满足 步骤1 的耗时比 步骤2+步骤3 的耗时长。这几乎不可能的。线程A在 步骤1和步骤4之间做了其他事情。那就是逻辑问题了。我们实际使用中,采用这种策略就能满足需要了。

可能有人要问了:

上述并发问题有办法解决么

有:可以采用异步延时删除策略,此时在 更新完数据库 到 删除缓存这段时间,数据可能稍微不一致。

有其他可能情况不一致么?

有:更新缓存失败!
方案:消息重试,可以接dts消息(binlog),这样缓存删除失败后,可以消息重试。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值