聊聊数据库和缓存一致性的几种实现方式

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介:

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!


缓存是互联网高并发系统里常用的组件,由于多增加了一层,如果没有正确的使用效果可能适得其反,诸如“缓存是删除还是更新?”,“先操作数据库还是先操作缓存?”都是些老生常谈的话题,今天我们就来聊一聊缓存与数据库的双写一致性的解决方案。

Cache Aside Pattern

在一开始先科普下最经典的缓存+数据库读写的模式,就是 Cache Aside Pattern。

  • 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
  • 更新的时候,先更新数据库,然后再删除缓存。

1

为什么是删除缓存,而不是更新缓存?

更新缓存在并发下会带来种种问题,直接删除缓存比较简单粗暴,稳妥。而且还有懒加载的思想,等用到的时候在去数据库读出来放进去,不用到你每次去更新他干嘛,浪费时间资源,而且还有更新失败、产生脏数据的一些风险, 达成这一点共识以后,我们来开始今天的讨论。

先更新数据库,再删除缓存

1、更新数据库成功,删除缓存成功,没毛病。

2、更新数据库失败,程序捕获异常,不会走到下一步,不会出现数据不一致情况。

3、更新数据库成功,删除缓存失败。数据库是新数据,缓存是旧数据,发生了不一致的情况。这里我们来看下怎么解决

  • 重试的机制,如果删除缓存失败,我们捕获这个异常,把需要删除的key发送到消息队列,然后自己创建一个消费者消费,尝试再次删除这个 key。
  • 异步更新缓存,更新数据库时会往 binlog 写入日志,所以我们可以通过一个服务来监听 binlog的变化(比如阿里的 canal),然后在客户端完成删除 key 的操作。如果删除失败的话,再发送到消息队列。

总之,我们要达到最终一致性!

先删除缓存,再更新数据库

1、删除缓存成功,更新数据库成功,没毛病。

2、删除缓存失败,程序捕获异常,不会走到下一步,不会出现数据不一致情况。

3、删除缓存成功,更新数据库失败,此时数据库中是旧数据,缓存中是空的,那么数据不会不一致。因为读的时候缓存没有,则读数据库中旧数据,然后更新到缓存中。

虽然没有发生数据不一致的情况,看上去好像一切都很完美,但是以上是在单线程的情况下,如果在并发的情况下可能会出现以下场景

1)线程 A 需要更新数据,首先删除了 Redis 缓存

2)线程 B 查询数据,发现缓存不存在,到数据库查询旧值,写入 Redis,返回

3)线程 A 更新了数据库

2

这个时候,Redis是旧的值,数据库是新的值,还是发生了数据不一致的情况。

延时双删

针对上面这种情况,我们有一种延时双删的方法

1)删除缓存

2)更新数据库

3)休眠 500ms(这个时间,依据读取数据的耗时而定)

4)再次删除缓存

3

你把旧值存在Redis以后,过一段时间我在删除一次,这时把旧值给删掉了,这样就能保证Redis和数据库是同步的了,这么做在一定程度上可以缓解这个问题,但也不是十分完美,比如第一次缓存删除成功了,第二次缓存删除失败,又该怎么办?

内存队列

除了延时双删这个方法,还有个方案就是内存队列,他的思想是串行化,我们在JVM中维护一个内存队列。当更新数据的时候,我们不直接操作数据库和缓存,而是把数据的Id放到内存队列;当读数据的时候发现数据不在缓存中,我们不去数据库查放到缓存中,而是把数据的Id放到内存队列。

后台会有一个线程消费内存队列里面的数据,然后一条一条的执行。这样的话,一个更新数据的操作,先删除缓存,然后再去更新数据库,但是还没完成更新。此时如果一个读请求过来,读到了空的缓存,那么先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成。

这里有一个优化点,一个队列中,其实多个更新缓存请求串在一起是没意义的,因此可以做过滤,如果发现队列中已经有一个更新缓存的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可。

等内存队列中将更新数据的操作完成之后,才会去执行下一个操作,也就是读数据的操作,此时会从数据库中读取最新的值,然后写入缓存中。

如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回;如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取。

总结

上面说的几种方案,都是比较常见的,也比较简单,没有十全十美的,最后的内存队列也会影响性能以及增加系统的复杂度。今天讨论的Redis和数据库的数据更新是不可能通过事务达到统一的,什么叫做事务,就是一损俱损一荣俱荣,要么都成功要么都失败,这是不能保证的。

我们只能根据相应的场景和所需要付出的代价来采取一些措施,降低数据不一致的问题出现的概率,在数据一致性和性能之间取得一个权衡,具体场景具体使用。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-06-09
本文作者:jack_xu
本文来自:“掘金”,了解相关信息可以关注“掘金”

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
17天前
|
消息中间件 canal 缓存
项目实战:一步步实现高效缓存与数据库的数据一致性方案
Hello,大家好!我是热爱分享技术的小米。今天探讨在个人项目中如何保证数据一致性,尤其是在缓存与数据库同步时面临的挑战。文中介绍了常见的CacheAside模式,以及结合消息队列和请求串行化的方法,确保数据一致性。通过不同方案的分析,希望能给大家带来启发。如果你对这些技术感兴趣,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!
51 6
项目实战:一步步实现高效缓存与数据库的数据一致性方案
|
25天前
|
canal 缓存 NoSQL
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
根据对一致性的要求程度,提出多种解决方案:同步删除、同步删除+可靠消息、延时双删、异步监听+可靠消息、多重保障方案
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
|
2月前
|
消息中间件 缓存 监控
如何保证缓存和数据库的一致性?
保证缓存和数据库的一致性的做法
|
25天前
|
存储 SQL 关系型数据库
一篇文章搞懂MySQL的分库分表,从拆分场景、目标评估、拆分方案、不停机迁移、一致性补偿等方面详细阐述MySQL数据库的分库分表方案
MySQL如何进行分库分表、数据迁移?从相关概念、使用场景、拆分方式、分表字段选择、数据一致性校验等角度阐述MySQL数据库的分库分表方案。
107 14
一篇文章搞懂MySQL的分库分表,从拆分场景、目标评估、拆分方案、不停机迁移、一致性补偿等方面详细阐述MySQL数据库的分库分表方案
|
9天前
|
消息中间件 缓存 NoSQL
15)如何保证缓存和数据库之间的数据一致性
15)如何保证缓存和数据库之间的数据一致性
20 1
|
1月前
|
消息中间件 缓存 NoSQL
奇怪的缓存一致性问题
本文记录了缓存一致性问题的排查过程和解决方案,同时带读者朋友们一起回顾下相关的八股文。
|
1月前
|
缓存 NoSQL 关系型数据库
MySQL与Redis缓存一致性的实现与挑战
在现代软件开发中,MySQL作为关系型数据库管理系统,广泛应用于数据存储;而Redis则以其高性能的内存数据结构存储特性,常被用作缓存层来提升数据访问速度。然而,当MySQL与Redis结合使用时,确保两者之间的数据一致性成为了一个重要且复杂的挑战。本文将从技术角度分享MySQL与Redis缓存一致性的实现方法及其面临的挑战。
66 2
|
2月前
|
SQL 存储 NoSQL
从SQL到NoSQL:理解不同数据库类型的选择与应用——深入比较数据模型、扩展性、查询语言、一致性和适用场景,为数据存储提供全面决策指南
【8月更文挑战第31天】在信息技术飞速发展的今天,数据库的选择至关重要。传统的SQL数据库因其稳定的事务性和强大的查询能力被广泛应用,而NoSQL数据库则凭借其灵活性和水平扩展性受到关注。本文对比了两种数据库类型的特点,帮助开发者根据应用场景做出合理选择。SQL数据库遵循关系模型,适合处理结构化数据和复杂查询;NoSQL数据库支持多种数据模型,适用于非结构化或半结构化数据。SQL数据库在一致性方面表现优异,但扩展性较差;NoSQL数据库则设计之初便考虑了水平扩展性。SQL使用成熟的SQL语言,NoSQL的查询语言更为灵活。
45 0
|
2月前
|
消息中间件 缓存 监控
go-zero微服务实战系列(六、缓存一致性保证)
go-zero微服务实战系列(六、缓存一致性保证)
|
2月前
|
存储 缓存 关系型数据库
Django后端架构开发:缓存机制,接口缓存、文件缓存、数据库缓存与Memcached缓存
Django后端架构开发:缓存机制,接口缓存、文件缓存、数据库缓存与Memcached缓存
37 0