redis和数据库的数据一致性

embedded/2024/11/14 17:14:23/

在我们使用redis作为缓存的时候,数据库缓存数据保持一致性就显得尤为重要,因为如果不做处理的话很有可能读取到的数据会出现差错,那这里怎么进行解决呢?

首先我们先来看一下操作数据到底是直接删除数据还是说通过修改的方式来修改数据呢?

如果是直接删除的话,那当然我们直接删除缓存的数据就行了,但是如果是更新 Redis 中的数据,可能会涉及到一系列复杂的业务逻辑计算,整个更新操作所需要付出的成本是比删除操作更高的,所以我们会选择直接删除数据。

那删除数据时先删缓存呢还是先删数据库中的数据呢?

先删缓存,再删数据库

我们先来看看先删缓存中的数据会出现什么问题:

整个步骤已经在图中进行标明,这里还是大概说一下步骤:

线程1去操作数据,先去缓存中删除数据,然后去数据库中进行更新,但是这个时候出现了网络延迟或其他问题,导致数据库的数据还没更新成功,线程2就开始从redis中进行读取数据;

线程2发现redis中没有这条数据,所以就去数据库中读,但是此时数据库中的数据依然是没有更新的数据,所以读取到的数据还是老数据,然后同步到redis当中;

这个时候线程1才将数据修改到数据库中,但是对于之后的线程读取的数据依旧是redis中的老数据,只有等到redis缓存过期之后,才会从数据库读到新的数据。

解决办法

延时双删

既然是因为缓存的原因,那我可不可以把缓存中的数据再删一次,然后去数据库里面读取新数据呢?这个想法是没毛病滴~

当线程2从数据库读取到数据之后,再把数据同步到redis当中,这个时候我们就需要做一个删除的操作,但是这个延迟删除的时间需要是等到线程1把数据同步到数据库中后,才进行删除,不然读到的还是旧数据,这个延时的时间需要你根据业务的整个执行时间去判断大概需要延时到多少~

当然,这个解决方法的话还是会出现部分请求到的数据依旧是老数据,但是最终结果上是解决了数据不一致的问题~

最终一致性和强一致性

所以这里还提供了一种方法,使用最终一致性和强一致性,这种就可以解决我们前几次还是会出现缓存不一致的问题。

你可以想想,因为redis数据库都是分布在不同的机器上分别进行处理的,即使是在同一台机器上执行也是需要两步操作才能完成,所以如果我们要保证操作缓存和操作数据库的原子性,就需要对他进行加锁。

但是我们加锁了肯定是会影响吞吐量的,但是我们设置缓存的目的就是减少系统吞吐量,这样一来加锁之后肯定会对系统性能造成影响,所以为了保证系统性能,我们还是不建议使用这种做法~

还留有另外一种操作数据的方法

先删数据库,再删缓存(推荐)

这张图也给出了一个大概步骤,我这里再梳理一下流程:

线程1操作数据,先去数据库中删除数据,然后再更新到混存当中,然后线程2从缓存中读取数据并返回。

但是这里会存在问题,你可以想一想,如果我的线程1在删除数据库数据的时候,因为网络延迟或者某些原因,还没有执行完成,这个时候缓存数据过期,线程2只能从数据库中获取数据,但是获取到的数据依旧是旧数据,然后线程1将数据库修改成功后再去删除缓存,然后线程2再把数据同步到缓存中,那这个时候缓存中的数据依旧是旧数据,从而导致数据一致性被破坏。

如果我数据库布置的是集群,那么主数据库写数据,从数据库读数据,如果这个时候我对主数据库写完数据后,从数据库还没来得及更新,那么其它线程来请求数据的时候,在从数据里面读取到的数据就是还未更新的数据,那么数据一致性也会被破坏掉~

解决办法

删除+延迟双删

和上一个提到的延迟双删一样,先删除一次缓存,然后再做一次延迟双删,当然什么时候做延迟双删的操作还是需要根据业务中的整个时间来进行判断,得等到我数据库修改完成再删除缓存后,再进行删除。

当然,频繁的删除操作对缓存是不友好的,很容易出现缓存击穿的问题~

删除失败怎么办

如果我们在删除缓存的时候删除失败了怎么办,就拿我们先更新数据库,在删缓存的场景来讲,我们可以借助消息队列设置一个重试机制,在重试达到我们设置的值之后,还是没能删除成功的话,我们可以把这个删除失败的消息通过消息中间件把消息返回给线程1,并发送告警给相关人员,从而人工介入~

如果是在高并发的情况下出现的这种情况,我们建议使用异步的方式来实现消息发送~

当然,使用这种方式的话,整个代码的耦合性还是很高的,如果想要降低耦合,我们可以引入一个组件——canal,但是引入组件后,整个系统的复杂性就会增加,这里就不多说了,感兴趣的小伙伴可以再深入了解一下~


http://www.ppmy.cn/embedded/137554.html

相关文章

单元测试、集成测试、系统测试、验收测试、压力测试、性能测试、安全性测试、兼容性测试、回归测试(超详细的分类介绍及教学)

目录 1.单元测试 实现单元测试的方法: 注意事项: 2.集成测试 需注意事项: 实现集成测试的方法: 如何实现高效且可靠的集成测试: 3.系统测试 实现系统测试的方法: 须知注意事项: 4.验收测试 实现验…

网络通信NetClient实现

网络通信NetClient实现 前言 上一集我们就完成了数据中心类的内容,那么我们开始需要进行网络的通信,我们这一集就要封装一个类来帮我们实现网络上的通信。 需求分析 我们这个NetClient类需要完成的事情其实也就那几件,我们之前规定了我们…

【QT常用技术讲解】优化网络链接不上导致qt、qml界面卡顿的问题

前言 qt、qml项目经常会涉及访问MySQL数据库、网络服务器,并且界面打开时的初始化过程就会涉及到链接Mysql、网络服务器获取数据,如果网络不通,卡个几十秒,会让用户觉得非常的不爽,本文从技术调研的角度讲解解决此类问…

【深度学习】使用硬件加速模型训练速度

一、 单机器单GPU 特点 配置简单:无需多 GPU 或分布式环境的复杂配置,适合资源有限的场景。适合小规模模型:对于计算量不大的模型(如中小型 CNN、RNN),单 GPU 可以处理大多数常见任务。 优势 简单易用&…

设计模式(四)装饰器模式与命令模式

一、装饰器模式 1、意图 动态增加功能,相比于继承更加灵活 2、类图 Component(VisualComponent):定义一个对象接口,可以给这些对象动态地添加职责。ConcreteComponent(TextView):定义一个对象,可以给这个对象添加一…

使用 Spring Security 实现基于角色的权限管理

在现代 Web 应用中,安全性是至关重要的一环。无论是基本的身份验证,还是复杂的访问控制,Spring Security 都能提供完善的解决方案。今天,我们将深入探讨如何在 Spring Boot 应用中配置 Spring Security,实现基于角色的…

基于ASP.NET+SQL Server实现简单小说网站(包括PC版本和移动版本)

一、网站简介 1.1 设计思路 根据一般人阅读小说的顺序,利用了HTML5、CSS3制作一个普通pc端和跨平台移动端。 PC端:小说的首页、小说某类具体信息、某小说详细信息页移动端:小说的首页、小说分类、小说某类具体信息、小说详情 1.2 网站的主…

数据库基础(9) . DML-多表操作

3.3.9.多表关联 student 表中 外键 team_id 指向 team表中主键 3.3.10.select子查询 3.3.10.1.实例1 select(select count(*) from student where stu_sex 1) as 男学生人数,(select count(*) from student where stu_sex 0) as 女学生人数;3.3.10.2.实例2 select team_i…