百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

MySQL与Redis缓存一致性的实现与挑战

mhr18 2024-11-21 18:06 25 浏览 0 评论

#头条创作挑战赛#

#醉鱼Java#

缓存是提高应用性能的重要手段之一,而 MySQL 和 Redis 是两种常用的数据存储和缓存技术。在许多应用中,常常将 Redis 用作缓存层,以加速对数据的访问。然而,在使用 MySQL 和 Redis 组合时,保持缓存与数据库之间的一致性是一个不得不考虑的问题。

一、缓存一致性的挑战

MySQL 和 Redis 之间的缓存一致性涉及到两个方面:

数据一致性

数据在 MySQL 和 Redis 中的一致性是指在对数据进行更新操作时,确保MySQL 和 Redis 中的数据保持同步。如果 Redis 中的缓存数据与 MySQL 数据库中的数据不一致,可能会导致应用程序出现错误以及一些未知的问题。

缓存有效性

缓存有效性是指 Redis 中的缓存数据是否仍然有效,是否需要更新或者过期。如果 Redis 中的缓存数据过期,但 MySQL 中的数据已经更新,可能会导致从 Redis 中获取到的数据不准确。

再说实现缓存与数据库数据一致性的实现方法之前,我们先来了解一下什么是缓存模式?

之前写过一篇关于缓存模式的文章,可以跳过去了解一下。

缓存模式

直接往下看也可以,我在这篇文章里面会重新对几种缓存模式进行一下介绍,并加以配图说明。

二、缓存模式有哪些

如果你读过上面缓存模式那篇文章的话,相信你对缓存模式应该有一定的了解了,如果不了解也没关系,我们一起来看看吧。

2.1、Cache Aside

最常用的缓存模式,大体意思是先从 cache 中取数据,没有获取到则从数据库中读取,成功后放到缓存中;

如果在 cache 中获取到数据直接返回;

更新时先把数据存到数据库,成功后再让缓存失效。

  • 先更新数据库,再更新缓存

遇到的问题是两个并发的更新操作,数据库先更新的后更新缓存,数据库后更新的先更新缓存,这样就会造成数据库与缓存的数据不一致,应用程序中读取的数据都是脏数据

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

遇到的问题是有两个并发操作,一个更新操作先删除了缓存,此时另一个并发的读取操作没有命中缓存,直接读取数据库并更新回了缓存,这个时候正好更新操作完成数据更新。此时数据库和缓存的数据不一致,应用程序读取的数据都脏数据了

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

这个方式也算是我们实际系统使用中比较推荐的一种方式,但是这种方式在理论上还是可能会出现问题,两个并发操作,其中一个查询操作没有命中缓存,此时查询出来了数据库中的老数据,此时另一个并发的更新操作,在刚才的并发读操作之后更新了数据库中的数据并删除了缓存,然后并发读操作线程又把老数据写入了缓存,此时又造成了数据的不一致,应用程序读取的都是脏数据。因为这种概率差生的情况实在是太小,所以才是我们系统中经常使用的一种方式了。

2.2、Read/Write Through

Cache Aside 模式中,应用程序需要维护两个数据存储,一个是缓存,一个是数据库,在 Read/Write Through 更新模式中,应用程序只需要维护缓存,数据库的维护工作就有缓存代理了

2.2.1、Read Through

Read Through 模式就是在查询时更新缓存,也就是说,在缓存失效时,Cache Aside 模式是由调用方负责把数据载入缓存,而 Read Through 模式是缓存服务自己更新缓存,自己来加载数据。

当应用程序执行读操作时,如果缓存中不存在所需数据,则缓存会自动从数据源(如数据库)中读取数据,并将数据加载到缓存中,然后返回给应用程序。

Read-Through 策略减少了应用程序与数据源之间的直接交互次数,提高了读操作的性能和响应速度。

2.2.2 Write Through

Write Through 和 Read Through 类似,当数据更新时,如果命中缓存则更新缓存,然后缓存更新数据库,这是一个同步的操作;如果没有命中缓存,直接更新数据库返回。

Write-Through 策略保证了缓存和数据源中的数据一致性,但由于每次写操作都需要等待数据源的确认,可能会影响写操作的性能和延迟。

2.3、 Write Behind Caching

Write Behind Caching 更新模式是在更新数据时只更新缓存,不更新数据库,而我们的缓存会异步的更新数据库。这个模式的话就是速度快,毕竟我们直接操作内存,因为是异步的,Write Behind Caching 更新模式还可以合并对同一个数据的多次操作到数据库,所以性能的提升也是很明显的

问题就是数据不是强一致性的,而且还可能会丢失,Write Behind Caching 更新模式实现逻辑复杂,因为它需要确认有哪些数据是被更新的,哪些数据是需要刷到持久层的数据库的。只有当缓存失效的时候才会把它真正的持久化起来。

Write-Behind 策略提高了写操作的性能和响应速度,但在写入缓存后,数据源中的数据可能会落后于缓存中的数据一段时间,存在一定的数据一致性风险。

三、一致性有哪些

说到一致性,我们应该想到的就是分布式系统中多个节点对看到的数据副本都保持一致的特性。换个说法就是,无论用户在哪个节点上执行操作,最终所有节点上的数据是相同的,且满足一定的约束条件。

在分布式系统中,实现一致性是很困难的,因为系统中的多个节点可能会因为网络延迟,节点故障或者其他的因素导致多个数据副本之间的状态不一致。为了实现分布式系统中的一致性,业界常用的算法和协议有 PaxosRaftZAB

分布式系统的一致性又分为强一致性、弱一致性、最终一致性。

  • 强一致性:最严格的一致性,相当于对于用户来说是最友好的。因为这个相当于系统写入的是什么数据,读取时也就是什么数据。
  • 弱一致性:相比于强一致性,弱一致性不承诺立即读取最新的值,也不承诺多久之后数据一致。但是会尽可能的保证在到达某个时间点之后,数据达到一致状态。
  • 最终一致性:最终一致性是弱一致性的一个特例,保证在一定时间之后达到数据一致状态,因此最终一致性也是目前业界来说最推崇的模型。

四、一致性实现方法

4.1、双写

双写其实就是 Write Through 模式,在写入 MySQL 数据库的同时,立即写入 Redis 缓存。这样可以确保 MySQL 和 Redis 中的数据保持一致,但增加了写入的延迟,并且增加了系统复杂度。

4.1.1、双写为什么先操作数据库在操作缓存?

我们来看如下的例子,线程A线程B 是一组并发请求。

1、线程A 发起一个写请求、先删除 Redis 中的缓存。

2、线程B 发起一个读请求,没有命中缓存。

3、线程B 读取数据库,获取数据。

4、线程B 写入老数据到缓存。

5、线程A 写入DB 新数据。

到这就发现了问题了吧,如果你没发现,那就跟着我的思路来看一下。

大家来看第三步,线程B 去读取数据库,此时 线程A 是还没有写入新数据的,所以此时 线程B 读取的数据是老数据。

而第5步,线程A 往数据库写入的才是最新的数据。

所以此时也就造成了数据不一致 了。

对于这种情况造成的脏数据缓存,有的小伙伴可能就提出来了,可以使用缓存双删啊,那么我们来继续往下看。

4.1.2、缓存延迟双删

延迟双删,就是字面意思,删除两边缓存。你知道问题在哪吗,想一想?

。。。。。。

双删,有可能删除失败吗?

删除失败怎么办?

双删的步骤如下

1、删除缓存。

2、更新数据库。

3、延迟删除(此时延迟的差不多是读请求的耗时多一点,防止读请求设置脏数据缓存)。

4.1.3、删除缓存的重试机制

删除缓存失败,第一个想到的应该是,删除失败了就多删除几次吧。

所以我们可以借助于消息队列,将删除失败的 key 加入到消息队列,对消息进行重试删除操作。

既然我们都用到消息队列了,那么我们为什么不直接监听 binlog 实现异步删除缓存呢。

4.2、消息队列

使用消息队列监听数据库变更事件、异步更新缓存。能够保证在数据库更新后,缓存能够按照顺序进行更新。

此时的例子就是,使用 Canal 监听 binlog ,发送数据到 MQ 中,应用程序监听 MQ 消息实现 Redis 缓存的更新。

五、总结

实现 MySQLRedis缓存的一致性中,需要考虑很多的方面,最直观的就是业务场景,性能要求,以及数据的安全等因素综合考虑。

例如,对于读多写少的场景,可以采用 Read/Write Through 模式;

而对于写操作频繁的场景,则可能需要考虑 Write Behind Caching 模式。

相关推荐

【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...

Pure Storage推出统一数据管理云平台及新闪存阵列

PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...

对Java学习的10条建议(对java课程的建议)

不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...

SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!

官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...

JDK21有没有什么稳定、简单又强势的特性?

佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...

「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了

在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...

Java面试题及答案最全总结(2025版)

大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...

数据库日常运维工作内容(数据库日常运维 工作内容)

#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...

分布式之系统底层原理(上)(底层分布式技术)

作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...

oracle 死锁了怎么办?kill 进程 直接上干货

1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...

SpringBoot 各种分页查询方式详解(全网最全)

一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...

《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略

《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...

LoadRunner(loadrunner录制不到脚本)

一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...

Redis数据类型介绍(redis 数据类型)

介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...

RMAN备份监控及优化总结(rman备份原理)

今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...

取消回复欢迎 发表评论: