前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >十分钟彻底搞懂缓存与数据库一致性的问题

十分钟彻底搞懂缓存与数据库一致性的问题

原创
作者头像
windealli
发布2024-01-04 12:23:06
3K3
发布2024-01-04 12:23:06
举报
文章被收录于专栏:windealli

导语

业务系统通常使用数据库(如MySQL)来存储持久化数据,并使用缓存(如Redis)来提升系统的性能。同时使用数据库和缓存,有一个老生常谈的问题,就是缓存与数据库一致性的问题。

本文将从缓存的应用场景、为什么会出现一致性问题、常见的解决方案等方面来帮助读者深入理解缓存与数据库一致性问题。

为什么需要缓存

一个简单的业务系统通常使用MySQL等关系型数据库作为存储系统

应用直接读写数据库。

在业务规模比较小时,对性能的要求也比较低,这样的架构一般可以正常运转。

但是随着业务规模的增长,数据库的吞吐量就无法满足业务的性能需要的。

一个常见的优化方案就是增加使用缓存来解决业务高并发读的问题。

将需要频繁读取的数据加载(存放)到缓存redis中,实现高性能读取。

缓存的三种设计模式

缓存有三种常见的设计模式: Cache Aside Pattern,Cache Through Pattern 和 Cache Back Pattern。

以下是对这三种模式的简要介绍:

模式一: Cache Aside Pattern(先更新数据在更新缓存):

  • 加载(Load): 应用程序从缓存读取数据,如果缓存不存在,则从数据库中把数据加载到缓存。
  • 更新(Update): 当数据发生变化时,应用程序负责更新数据库,并清除或更新相应的缓存项。这保持了缓存中的数据与数据库的一致性。
  • 优点: 简单、易于理解和实现。缓存中的数据是惰性加载的,减少了对缓存的依赖。
  • 缺点: 应用程序需要管理缓存,导致了代码中可能存在一致性维护的复杂性。

模式二: Cache Through Pattern (基于DTS)

  • 加载(Load): 应用程序通过缓存接口请求数据,而不直接从数据库读取。如果缓存中不存在所需数据,缓存会负责从数据库中加载数据,然后返回给应用程序。
  • 更新(Update): 与 Cache Aside 不同,Cache Through 模式中更新数据时,缓存并不直接参与。应用程序负责将更新操作发送到数据库,然后数据库负责更新数据(并通过DTS等方式将数据同步到缓存中),保持数据一致性。
  • 优点: 简化了应用程序对缓存的管理,数据库更直接地负责更新操作,减轻了应用程序的负担。
  • 缺点: 数据更新时,可能引起缓存和数据库之间的不一致,需要额外的机制来维护一致性。

模式三: Cache Back Pattern (先更新缓存再更新数据库)

  • 加载(Load): 应用程序通过缓存接口请求数据,缓存负责检查是否有缓存命中。如果缓存未命中,缓存会从数据库中加载数据并返回给应用程序。
  • 更新(Update): 当数据需要更新时,应用程序负责将更新发送到缓存,缓存负责将更新异步地传递给数据库,以保持数据的一致性。
  • 优点: 提供了更好的性能,因为应用程序可以更快地获取数据。同时,通过异步更新数据库,降低了对数据库的直接压力。
  • 缺点: 引入了异步处理,可能会在一定程度上牺牲一致性。系统中需要处理缓存和数据库之间的同步问题。

其中, Cache Aside Pattern 是最常用的设计模式

为什么会有一致性问题

在使用缓存的三种设计模式中, Cache Through Pattern 因为系统本身不负责缓存的更新,通常需要较为复杂的机制来保证数据的一致性。

大多数业务系统通常会使用Cache Aside Pattern , 本文将重点讨论 Cache Aside PatternCache Back Pattern 的一致性问题。

下面是缓存与数据库数据不一致的四种场景:

注意更新缓存有两种方式:

  1. 更新数据时,直接更新(写入)缓存;
  2. 更新数据时,只删除缓存,在读取时才更新(写入)缓存;

场景一: 先更新数据库再更新缓存Cache Aside Pattern

高并发时,会出现不一致行为。假设请求 A 先操作数据库,请求 B 后操作数据库,但是可能存在请求 B 先写缓存,请求 A 后写缓存的情况,从而导致数据库与缓存之间的数据不一致。

场景二: 先更新数据库再删除缓存Cache Aside Pattern

出现缓存不一致的概率较低,一般是删除缓存失败才会导致最终的不一致。

场景三: 先更新缓存再更新数据库Cache Back Pattern

业务无法保证两个写操作都成功,如果数据库更新失败,则数据会最终不一致。

场景四: 先删除缓存在更新数据库Cache Back Pattern

高并发时,会出现不一致情况。如果一个请求在删除缓存和更新数据库之间来了一个新的读请求,又把旧的数据加载到缓存,则会发生数据不一致。

总结: 可以发现,不管先写缓存还是先写数据库,都有可能出现数据不一致。

如何解决缓存与数据库的一致性问题

从上面出现缓存与数据库不一致的几种场景,可以归纳缓存与数据库不一致的主要原因是:

  • 并发
  • 两步更新操作非原子性,第二步可能失败。

对于并发引起的一致性问题,可以通过延时双删来解决。

对于原子性问题引起的一致性问题,则是想办法确保第二步操作最终成功。

方案一: 延时双删策略

  1. 删除缓存
  2. 更新数据库
  3. 休眠一段时间(如1s),再次删除缓存。

延时双删策略中:

  • 一方面,如果在删除缓存和更新数据库的空挡,有其他的读请求加载了旧数据到缓存,第二次删除也可以修正数据。
  • 另一方面,如果并发没有特别高,那么业务侧旧能较快地在缓存中获取到新数据。

方案二: 重试补偿

对于原子性问题引起的一致性问题,通常想到的简单方法就是通过重试、补偿等机制来解决。

  1. 更新数据库
  2. 更新缓存(失败)
  3. 再次更新缓存。

重试补偿方案存在不足:

  1. 重试操作大概率会再次失败
  2. 重试的次数如何选择
  3. 如果在更新数据库和更新缓存之间程序Crash, 则不会有补偿机会。

方案三: 基于MQ的异步更新缓存策略

对于原子性问题引起的一致性问题,在补偿的基础上,可以将重试补偿的动作转为异步,从而修正重试补偿方案的不足。

引入消息队列主要是基于消息队列的下列特性

  • 保证可靠性:写到队列中的消息,成功消费之前不会丢失
  • 保证消息成功投递:消息成功消费后才会被删除,满足重试需要。

注意:异步更新只能是删除操作,不可以是写操作

方案四:基于binlog的异步更新缓存策略

基于MQ的异步更新缓存策略因为使用了消息队列组件,引入新的问题:

  • 写队列失败:写消息队列也可能出现失败。(写缓存和写消息队列同时失败概率其实很小)

MySQL等数据库通常自身保证了数据的一致性,并提供了binlog。 binlog不会出现丢失问题,

可以利用binlog来解决消息丢失(未成功写入)的问题。

其他问题

强一致性问题

上文提到的缓存与数据库一致性问题的几种解决方案都不是完美的,可以看出,只能保证数据的最终一致性,是无法保证强一致性的。

能否保证强一致性呢,通过Paxos、Raft 等一致性协议是可以做到到,但可能并非业务侧想要的。

基于CAP理论,一个业务系统只能在一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)最多满足两个。

使用缓存和数据库,相当于已经实现了P,因此我们的系统只能是一个

  • CP系统:保证强一致性,但是牺牲可用性
  • AP系统:保证可用性,牺牲一致性(只做最终一致)

如果实现CP, 系统的可用性(性能)会受到比较大的影响。对于大多数系统而言,引入缓冲只是为了提升读取的性能,如果最终结果反而牺牲可用性(牺牲性能)那就本末倒置了。

因此通常业务系统不会选择CP,不去强求强一致性。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导语
  • 为什么需要缓存
  • 缓存的三种设计模式
    • 模式一: Cache Aside Pattern(先更新数据在更新缓存):
      • 模式二: Cache Through Pattern (基于DTS)
        • 模式三: Cache Back Pattern (先更新缓存再更新数据库)
        • 为什么会有一致性问题
          • 场景一: 先更新数据库再更新缓存Cache Aside Pattern
            • 场景二: 先更新数据库再删除缓存Cache Aside Pattern
              • 场景三: 先更新缓存再更新数据库Cache Back Pattern
                • 场景四: 先删除缓存在更新数据库Cache Back Pattern
                • 如何解决缓存与数据库的一致性问题
                  • 方案一: 延时双删策略
                    • 方案二: 重试补偿
                      • 方案三: 基于MQ的异步更新缓存策略
                        • 方案四:基于binlog的异步更新缓存策略
                        • 其他问题
                          • 强一致性问题
                          相关产品与服务
                          云数据库 Redis
                          腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档