保持DB和缓存一致性


​ 对于维持DB和缓存的一致性,我们先假设更新数据库和更新缓存都可以成功的情况。

​ 主要有以下几种方法:

Cache Aside Pattern

​ 对于一致性更新,先把数据存到数据库中,成功后再让缓存失效

​ 这时,对于并发的更新和查询操作,并发查询操作拿到的是脏数据,而更新成功后的查询操作都是更新后的数据。

​ 不过理论上也有一直出现脏读的可能性:并发读和写,读操作缓存不命中,读操作从数据库取数据。此时写操作修改完数据库,让缓存失效。然后之前读的操作把脏数据存入缓存,造成缓存中脏数据存在。(可能性特别低,因为写操作比读操作慢很多,读操作在写操作前进入数据库操作并在写操作结束后就结束可能性特别低)

Read/Write Through Pattern

​ 应用认为后端就是一个单一的存储,后台数据库自己维护缓存。其中Read Through Pattern策略是在查询时更新缓存(当缓存失效的时候,后台服务器自动加载),而Write Through Pattern策略是在更新时更新缓存(当有数据更新时,如果没有命中缓存,则更新数据库并发返回。否则更新缓存再由缓存更新数据库)

Write Behind Caching Pattern

​ 在更新数据的时候,只更新缓存,不更新数据库,然后缓存会异步地批量更新数据库。这样效率非常快。缺点是数据库和缓存不能保持强一致性,还可能造成数据丢失

mysql更新缓存方式

​ 先更新缓存,然后异步把数据写到磁盘上。为了保证数据不丢失,采用WAL方法:数据进来后先把数据以log方式写到磁盘(由于是顺序写,所以很快)。写完后进行更新缓存操作,若出现死机等情况,重启后可以从log文件中重新把数据读出来然后再更新到缓存,最后异步更新到数据库。

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

<think>好的,我现在需要处理用户关于如何保证数据库缓存一致性的问题。用户提到了解决方案或最佳实践,我需要结合之前的引用内容来构建回答。首先,我应该回忆之前提供的引用内容。引用1提到了几种方式,推荐先写MySQL再删除Redis,或者使用binlog消息队列。引用23都讨论了Cache Aside模式,强调写操作先更新数据库再删除缓存,以及原子性操作的重要性。接下来,我需要确定回答的结构。用户需要最佳实践方法,所以分点说明比较清晰。常见的策略包括CacheAside、Read/Write Through、WriteBehind等,但根据引用内容,CacheAside是重点,尤其是先更新数据库再删除缓存的步骤。 另外,引用提到的实时一致性最终一致性的区别需要涵盖。用户可能需要了解不同场景下的选择,比如实时性要求高时用先写库再删缓存,而最终一致性可以用binlog异步处理。 还要注意处理并发问题,比如延迟双删、重试机制,以及如何确保操作的原子性。例如,引用3提到删除缓存失败时的重试策略,比如消息队列或订阅binlog。需要避免常见的误区,比如先删缓存再更新数据库,这可能导致脏数据。同时,兜底方案如超时剔除也很重要,引用2中提到了设定超时时间。最后,生成相关问题,可能涉及不同策略的比较、具体实现细节或实际应用案例。需要确保回答结构清晰,分点列出策略,并结合引用中的例子说明。同时,引用标识要自然地加在相关段落末尾,例如CacheAside部分引用3,双删策略引用2等。</think>### 如何保证数据库与缓存一致性的最佳实践方法 保证数据库与缓存一致性是分布式系统的核心挑战之一。以下是业界广泛采用的解决方案最佳实践,结合不同场景需求进行选择: --- #### 一、核心策略:Cache Aside(旁路缓存)模式 **适用场景**:读多写少、对实时一致性要求较高的场景[^3]。 **实现步骤**: 1. **读操作**: - 先读缓存,若命中则直接返回数据。 - 若未命中,则查询数据库,将结果写入缓存并设置超时时间(兜底方案)。 $$ \text{缓存命中率} = \frac{\text{有效缓存请求数}}{\text{总请求数}} $$ 2. **写操作**: - **先更新数据库**,确保数据持久化。 - **再删除缓存**(而非更新缓存),避免并发写导致脏数据[^3]。 **优势**: - 减少缓存与数据库的强耦合,降低因并发操作导致数据不一致的风险。 - 通过删除缓存而非更新,避免其他线程在更新间隙读到旧数据。 --- #### 二、增强一致性的关键优化 1. **延迟双删(Delayed Double Delete)** - 在更新数据库后,先删除缓存,等待短暂延迟(如几百毫秒),再次删除缓存。 - **目的**:防止其他线程在数据库更新完成前读取旧数据并重建缓存[^2]。 2. **异步重试机制** - 若缓存删除失败,通过消息队列(如Kafka)或订阅数据库的`binlog`(如Canal工具)异步重试删除操作[^1]。 - **示例流程**: ```text 更新数据库 → 写入binlog → 消息队列消费 → 删除缓存 ``` 3. **原子性保障** - 使用分布式事务(如Seata)或数据库事务确保“更新数据库+删除缓存”的原子性。 - 若无法实现原子性,需通过日志补偿或告警机制监控失败操作。 --- #### 三、一致性级别的选择 1. **强一致性** - **方案**:先更新数据库,同步删除缓存(需保证原子性)。 - **代价**:性能较低,适用于金融、库存等对一致性要求极高的场景。 2. **最终一致性** - **方案**:通过`binlog`异步同步数据,结合消息队列重试[^1]。 - **优势**:对业务侵入性低,吞吐量高,适合电商、社交等场景。 --- #### 四、兜底方案设计 1. **缓存设置超时时间** - 即使主动更新失败,超时剔除能强制回源数据库,避免长期不一致[^2]。 - 公式:$$ T_{\text{超时}} = \text{业务容忍时间} + \text{网络延迟缓冲} $$ 2. **降级策略** - 直接读数据库并返回,跳过缓存(如缓存故障时)。 --- #### 五、不推荐方案 1. **先删缓存再更新数据库** - 风险:其他线程可能在数据库更新前读取旧值并重建缓存,导致长期不一致。 2. **直接更新缓存** - 问题:高并发写时可能覆盖彼此的值,且难以保证事务性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值