业务系统通常使用数据库(如MySQL)来存储持久化数据,并使用缓存(如Redis)来提升系统的性能。同时使用数据库和缓存,有一个老生常谈的问题,就是缓存与数据库一致性的问题。
本文将从缓存的应用场景、为什么会出现一致性问题、常见的解决方案等方面来帮助读者深入理解缓存与数据库一致性问题。
一个简单的业务系统通常使用MySQL等关系型数据库作为存储系统
应用直接读写数据库。
在业务规模比较小时,对性能的要求也比较低,这样的架构一般可以正常运转。
但是随着业务规模的增长,数据库的吞吐量就无法满足业务的性能需要的。
一个常见的优化方案就是增加使用缓存来解决业务高并发读的问题。
将需要频繁读取的数据加载(存放)到缓存redis中,实现高性能读取。
缓存有三种常见的设计模式: Cache Aside Pattern,Cache Through Pattern 和 Cache Back Pattern。
以下是对这三种模式的简要介绍:
其中, Cache Aside Pattern 是最常用的设计模式
在使用缓存的三种设计模式中, Cache Through Pattern
因为系统本身不负责缓存的更新,通常需要较为复杂的机制来保证数据的一致性。
大多数业务系统通常会使用Cache Aside Pattern
, 本文将重点讨论 Cache Aside Pattern
和Cache Back Pattern
的一致性问题。
下面是缓存与数据库数据不一致的四种场景:
注意更新缓存有两种方式:
Cache Aside Pattern
高并发时,会出现不一致行为。假设请求 A 先操作数据库,请求 B 后操作数据库,但是可能存在请求 B 先写缓存,请求 A 后写缓存的情况,从而导致数据库与缓存之间的数据不一致。
Cache Aside Pattern
出现缓存不一致的概率较低,一般是删除缓存失败才会导致最终的不一致。
Cache Back Pattern
业务无法保证两个写操作都成功,如果数据库更新失败,则数据会最终不一致。
Cache Back Pattern
高并发时,会出现不一致情况。如果一个请求在删除缓存和更新数据库之间来了一个新的读请求,又把旧的数据加载到缓存,则会发生数据不一致。
总结: 可以发现,不管先写缓存还是先写数据库,都有可能出现数据不一致。
从上面出现缓存与数据库不一致的几种场景,可以归纳缓存与数据库不一致的主要原因是:
对于并发引起的一致性问题,可以通过延时双删来解决。
对于原子性问题引起的一致性问题,则是想办法确保第二步操作最终成功。
延时双删策略中:
对于原子性问题引起的一致性问题,通常想到的简单方法就是通过重试、补偿等机制来解决。
重试补偿方案存在不足:
对于原子性问题引起的一致性问题,在补偿的基础上,可以将重试补偿的动作转为异步,从而修正重试补偿方案的不足。
引入消息队列主要是基于消息队列的下列特性
注意:异步更新只能是删除操作,不可以是写操作
基于MQ的异步更新缓存策略因为使用了消息队列组件,引入新的问题:
MySQL等数据库通常自身保证了数据的一致性,并提供了binlog。 binlog不会出现丢失问题,
可以利用binlog来解决消息丢失(未成功写入)的问题。
上文提到的缓存与数据库一致性问题的几种解决方案都不是完美的,可以看出,只能保证数据的最终一致性,是无法保证强一致性的。
能否保证强一致性呢,通过Paxos、Raft 等一致性协议是可以做到到,但可能并非业务侧想要的。
基于CAP理论,一个业务系统只能在一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)最多满足两个。
使用缓存和数据库,相当于已经实现了P,因此我们的系统只能是一个
如果实现CP, 系统的可用性(性能)会受到比较大的影响。对于大多数系统而言,引入缓冲只是为了提升读取的性能,如果最终结果反而牺牲可用性(牺牲性能)那就本末倒置了。
因此通常业务系统不会选择CP,不去强求强一致性。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。