种一棵树最好的时间是十年前,其次是现在.|

luolin1024

园龄:5年4个月粉丝:1关注:0

如何保证数据库与缓存双写一致:策略解析与实践指南

在当今分布式架构中,缓存因其高并发、高性能等特点广泛应用。通常,在读取缓存时,我们遵循如下流程:

image.png

然而,当涉及到数据更新时,究竟是先更新缓存还是先更新数据库?或者在更新时是否需要删除缓存?这些问题都需要在实际场景中仔细考量。本文将详细探讨几种常见的缓存更新策略,并对不完善之处进行补充说明。

一、理论基础:缓存过期策略

从理论上讲,为缓存设置过期时间是一种确保最终数据一致性的手段。

  • 原理:对写操作始终以数据库为准,缓存仅作为加速手段。如果数据库更新成功,而缓存更新失败,只要数据在缓存中的过期时间到了,后续读请求就会从数据库中读取最新数据并重新填充缓存。
  • 局限:该方法虽然能保证最终一致性,但在业务对实时性要求较高的场景下,依赖过期时间可能会引发短时间内的数据不一致问题。

因此,本文接下来的讨论将不依赖缓存自动过期,而是介绍在业务层面主动保证数据一致性的几种更新策略。

二、常见的缓存更新策略

1. 更新数据库后直接更新缓存

流程

  1. 更新数据库。
  2. 紧接着更新缓存。

问题分析

  • 线程安全性不足:假设两个请求 A 和 B 同时进行数据更新,可能出现如下情况:
    • A 请求先将新数据写入缓存,随后 B 请求更新数据库,但由于更新缓存操作延迟,缓存中依然保存 A 的数据。
    • 最终,数据库中存储的是 B 的数据,而缓存中却仍然保留 A 的数据,导致脏数据出现。
    • image.png
  • 性能浪费:在写操作频繁而读操作较少的场景下,频繁更新缓存反而增加了系统的额外负载,得不偿失。

总结:该策略在实际生产环境中通常不被推荐,除非能确保并发更新的严格顺序和线程安全性。


2. 先删除缓存后再更新数据库

流程

  1. 删除缓存数据;
  2. 更新数据库数据。

潜在风险
设想存在如下并发场景:

  • 请求 A 执行更新操作,先删除缓存。
  • 请求 B 此时执行查询操作,因缓存已被删除,从数据库中读取旧数据,并将其回写到缓存中。
  • 请求 A 随后更新数据库成功,但缓存中依然存在 B 回填的旧数据,最终导致数据库和缓存数据不一致。
  • image.png

补充改进:延时双删策略
为解决上述问题,可以引入延时双删

  • 在更新数据库后,除了立即删除缓存外,再过一段设定的延时后再次执行删除操作。
  • 此举可确保那些因并发查询而意外写入的脏数据能够被及时清除。
  • 如延时删除依然失败,可以通过引入消息队列(MQ)等重试机制进行异步处理。

3. 更新数据库后直接删除缓存(延迟删除策略)

流程

  1. 先更新数据库数据;
  2. 数据库更新成功后,删除缓存数据。

优势与原理
国外提出的 Cache-Aside Pattern 就是这种思路,主要包含以下三个步骤:

  • 失效:应用程序首先尝试从缓存中获取数据,若未命中则从数据库中加载数据后写入缓存;
  • 命中:直接从缓存返回数据;
  • 更新:先将数据写入数据库,成功后使缓存失效(删除缓存)。

Facebook 在论文 Scaling Memcache at Facebook 中也采用了这种先更新数据库再删除缓存的策略。与“先删除缓存再更新数据库”的方式相比,它少了一次删除缓存操作,从而能有效降低因并发操作而引起的脏数据风险。

可能出现的极端情况
尽管理论上这种方式能保持数据一致,但在如下极端场景下仍可能产生问题:

  1. 请求 A 发起查询操作时,缓存恰好失效;
  2. 请求 A 从数据库中读取到旧数据并将其写入缓存;
  3. 请求 B 正在更新数据库并随后删除缓存;
  4. 如果请求 A 的读取和回写操作延迟超过 B 的缓存删除时间,则有可能将旧数据再次写入缓存。

但实际上,由于数据库的读操作通常比写操作快,这种情况的发生概率较低,通过适当的系统调优可以进一步降低风险。


4. 基于日志监听的异步删除缓存

核心思想
利用数据库的变更日志(如 BigLog 或类似系统)实现缓存的异步删除,具体流程如下:

  1. 当数据库更新后,不立即同步更新缓存;
  2. 通过监听数据库变更日志,捕捉数据变更事件;
  3. 根据变更事件异步删除相应的缓存数据,确保后续查询能够获取最新的数据。

优势

  • 异步化处理:降低了前端更新时的延时,减小了因并发操作引起的不一致概率。
  • 高容错性:结合消息队列等机制,可以实现对删除失败的重试,从而更好地保障数据一致性。

挑战

  • 实现复杂度较高,需要部署和维护日志监听、消息队列等中间件。
  • 在高并发场景下,异步延迟可能会导致短暂的不一致,需根据业务需求合理调整延时策略和重试机制。

三、总结与实践建议

不同场景下,选择合适的缓存更新策略至关重要,以下是几点建议:

  • 读多写少场景
    缓存能显著提升读取性能。推荐采用“更新数据库后直接删除缓存”的策略,并结合延时删除机制,降低并发时脏数据产生的可能性。

  • 写操作频繁场景
    为减少频繁更新缓存带来的性能开销,可以考虑使用“延时双删”策略或基于日志监听的异步删除方式,以确保数据在并发写入情况下的最终一致性。

  • 综合保障
    除了更新策略之外,合理设置缓存过期时间作为补充手段,也是确保数据一致性的重要措施。此外,针对具体业务场景,可引入分布式锁、幂等性设计等手段进一步提升系统的可靠性。

总之,在设计分布式缓存更新方案时,需要权衡性能、实时性与数据一致性三者之间的关系,选择最适合业务需求的策略,并根据实际运行情况不断进行调优和优化。

posted @   luolin1024  阅读(17)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起