如何保持数据库和缓存的一致性

本文介绍了更新缓存的三种策略:失效、命中和更新。讨论了写操作中先使缓存失效后更新数据库、先更新数据库再更新缓存可能导致的数据不一致问题。还给出了更新数据库成功但失效缓存失败的解决办法,如重试、定期全量更新、设置失效期等。

更新缓存策略
失效:应用程序从cache取数据,没有得到,则从数据库取数据,然后将数据放入cache 
命中:应用从cache取数据,若取到则返回 
更新:把数据更新到数据库,然后使cache失效 
借用网上两张图 
è¿éåå¾çæè¿°

è¿éåå¾çæè¿°
有没有其他策略?
读操作容易理解,现在讨论一下写的操作

如果先使cache失效,后更新数据库,是否可行
这么做引发的问题是,如果A,B两个线程同时要更新数据,并且A,B已经都做完了删除缓存这一步,接下来,A先更新了数据库,C线程读取数据,由于缓存没有,则查数据库,并把A更新的数据,写入了缓存,最后B更新数据库。那么缓存和数据库的值就不一致了。

如果先把数据库更新,然后把cache更新(不是失效),是否可行
这么做引发的问题是,如果A,B两个线程同时做数据更新,A先更新了数据库,B后更新数据库,则此时数据库里存的是B的数据。而更新缓存的时候,是B先更新了缓存,而A后更新了缓存,则缓存里是A的数据。这样缓存和数据库的数据也不一致。

如果更新数据时,更新数据库成功,而失效cache时失败怎么解决?
1、对删除缓存进行重试,数据的一致性要求越高,我越是重试得快。 
2、定期全量更新,简单地说,就是我定期把缓存全部清掉,然后再全量加载。 
3、给所有的缓存一个失效期。(绝招,任何不一致,都可以靠失效期解决,失效期越短,数据一致性越高。但是失效期越短,查数据库就会越频繁。因此失效期应该根据业务来定)

不要缓存那些对数据一致性要求很高的数据
--------------------- 

原文:https://blog.youkuaiyun.com/sinat_16712671/article/details/76130829 
 

<think>好的,用户现在问的是如何保持数据库缓存的一致性。之前他们问过Spring Boot的启动过程,现在转向数据库缓存一致性,可能是在开发后端应用时遇到的实际问题。需要先理解他们的使用场景,可能是在高并发或者需要快速读写的系统中使用缓存,比如Redis,来提升性能,但随之而来的数据一致性问题需要解决。 用户可能是开发人员,可能对缓存策略有一定了解,但想深入理解如何确保一致性。需要分析常见的缓存更新策略,比如Cache-Aside、Read/Write Through、Write Behind等,以及它们的优缺点。同时要考虑异常情况,比如更新数据库后更新缓存失败,或者并发写导致的数据不一致。 需要分步骤讲解,可能用户需要具体的解决方案,比如延迟双删、使用消息队列确保最终一致性,或者通过分布式锁来避免并发问题。还要考虑实际应用中的权衡,比如一致性性能之间的平衡,是否需要强一致性还是最终一致性即可。 用户可能没有提到具体场景,但需要覆盖常见的情况,比如先更新数据库还是先更新缓存,如何失效缓存,处理缓存穿透、雪崩等问题。需要结构清晰,给出多种方案,并说明适用场景,帮助用户根据实际情况选择合适的方法。 最后,要确保语言通俗易懂,避免过于技术化的术语,但也要准确。可能需要用示例或流程图来辅助说明,比如双写策略的步骤,或者使用版本号解决并发问题的方法。总结时强调没有银弹,需要根据业务需求选择合适的策略,并做好监控异常处理。</think>--- ### 数据库缓存一致性保障方案 数据库缓存的一致性问题主要出现在 **缓存更新策略** **并发操作** 场景中,需结合业务需求选择策略。以下是核心解决思路: --- #### 一、**常见问题场景** 1. **缓存失效后,数据库更新导致缓存数据库不一致** 2. **并发读写时,缓存数据库更新顺序冲突** 3. **缓存更新失败,残留旧数据** --- #### 二、**核心解决方案** ##### 1. **缓存更新策略** - **(1) Cache-Aside(旁路缓存)** - **读操作**:先读缓存,未命中则读数据库并回填缓存。 - **写操作**:直接更新数据库,然后 **删除缓存**(非更新缓存)。 - **优点**:简单,减少脏数据风险。 - **风险**: - **并发读写不一致**:A 写数据库后删除缓存前,B 读旧数据回填缓存。 - **解决方案**:延迟双删(先删缓存→更新数据库→延迟再删缓存)。 ```java // 伪代码示例:延迟双删 public void updateData(Data data) { cache.delete(data.id); // 先删除缓存 db.update(data); // 更新数据库 Thread.sleep(500); // 延迟(根据业务设置时间) cache.delete(data.id); // 再次删除可能残留的旧缓存 } ``` - **(2) Write-Through(直写)** - **写操作**:先更新缓存,再同步更新数据库。 - **优点**:保证缓存数据库强一致。 - **缺点**:写性能较低,需依赖缓存数据库的原子性。 - **(3) Write-Behind(异步写)** - **写操作**:先更新缓存,异步批量更新数据库。 - **优点**:高吞吐,适合写多读少场景。 - **缺点**:存在数据丢失风险(缓存宕机)。 --- ##### 2. **并发场景优化** - **分布式锁** - 对同一数据的读写操作加锁,避免并发冲突。 - **示例**:使用 Redis 的 `SETNX` 或 RedLock 实现互斥锁。 - **版本号/时间戳** - 数据更新时携带版本号,缓存中只接受更高版本的数据。 --- ##### 3. **最终一致性保障** - **消息队列(MQ)异步补偿** - 数据库更新后,发送 MQ 消息触发缓存删除。 - **容错机制**:重试 + 死信队列监控。 - **监听数据库 Binlog** - 通过 Canal 或 Debezium 监听数据库变更日志,触发缓存更新/删除。 - **优点**:解耦业务代码,适用于复杂系统。 --- #### 三、**方案选择与权衡** | **策略** | **一致性强度** | **性能** | **复杂度** | **适用场景** | |------------------|----------------|----------|------------|---------------------------| | Cache-Aside | 最终一致 | 高 | 低 | 读多写少 | | Write-Through | 强一致 | 中 | 中 | 写操作较少,强一致性要求 | | Write-Behind | 弱一致 | 极高 | 高 | 写密集,允许短暂不一致 | | Binlog 监听 | 最终一致 | 中 | 高 | 高可用分布式系统 | --- #### 四、**关键注意事项** 1. **避免同时更新缓存数据库**:优先保证数据库正确性,缓存可重建。 2. **设置缓存过期时间**:即使出现不一致,也能通过过期自动恢复。 3. **降级与监控**:缓存故障时,需有降级策略(如直接读库)。 --- ### 总结 $$ \text{目标} = \text{业务容忍度} \cap \text{性能} \cap \text{复杂度} $$ - **弱一致性场景**:优先选择 Cache-Aside + 延迟双删。 - **强一致性场景**:需引入分布式锁或同步写策略。 - **最终一致性场景**:结合 Binlog 监听或 MQ 异步补偿。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值