Redis学习二

Redis和数据库数据一致性问题

Redis作为缓存分两种情形

  1. 只读缓存, 只读缓存无需考虑数据更新问题, Redis中有则返回Redis中的数据, Redis无则查询数据库
  2. 读写缓存
    • 同步直写策略
    • 异步缓写策略

数据读取流程:

正常回写Redis代码流程:

public Object getDataById(String id) {// 1. 先去Redis中查询Object obj;obj = redisTemplate.opsForValue().get(DATA_PR + id);if (obj == null) {// 2. redis中查询出来为null则查询数据库obj = testMapper.getDataById(id);if (obj == null) {// 3.1redis, 数据库都没有数据// 可以给这个id给个空值存在Redis中redisTemplate.opsForValue().set(DATA_PR + id, "null");} else {// 3.2 数据库中有数据将数据写回RedisredisTemplate.opsForValue().set(DATA_PR + id, obj);}}return obj;}

保证并发时, 避免同一数据频繁写入Redis对回写缓存代码进行加锁

public Object getDataById1(String id) {// 1. 先去Redis中查询Object obj;obj = redisTemplate.opsForValue().get(DATA_PR + id);if (obj == null) {// 缓存不存在则加锁// 假设请求量很大synchronized (TestServiceImpl.class) {obj = redisTemplate.opsForValue().get(DATA_PR + id);if (obj != null) {// 查询有数据则直接返回return obj;} else {// 没有数据再查询数据库obj = testMapper.getDataById(id);// 回写缓存if (obj != null) {redisTemplate.opsForValue().setIfAbsent(DATA_PR + id, obj, 20, TimeUnit.SECONDS);return obj;} else {return null;}}}}return obj;}

给缓存设置过期时间, 定期清理缓存并回写, 是保证最终一致性的解决方案

我们可以对存入缓存的数据设置过期时间, 所有的写操作以数据库为准, 对缓存的操作只是尽最大努力即可, 也就是说数据库写成功缓存更新失败, 那么只要达到过期时间, 则后面的读请求自然会从数据库读取新值然后回填缓存,达到一致性, 要以数据库写入库为准

更新数据库并更新Redis有以下几种情况

  1. 先更新数据库再更新Redis

    这种情况会出现的问题:

    请求A先将字段x更新为10

    请求B后将字段x更新为20

    正常流程:

    请求A更新数据库将x更新为10

    请求A更新Redis 将x更新为10

    请求B更新数据库将x更新为20

    请求B更新Redis 将x更新为20

    异常情况:

    请求A更新数据库将x更新为10

    请求B更新数据库将x更新为20

    请求B更新Redis 将x更新为20

    请求A更新Redis 将x更新为10

    此时数据库x为10, Redis中x为20出现缓存和数据库数据不一致情况

  2. 先更新Redis再更新数据库

    这种情况会出现的问题:

    请求A先将字段x更新为10

    请求B后将字段x更新为20

    正常流程:

    请求A更新Redis 将x更新为10

    请求A更新数据库将x更新为10

    请求B更新Redis将x更新为20

    请求B更新数据库将x更新为20

    异常情况:

    请求A更新Redis 将x更新为10

    请求B更新Redis将x更新为20

    请求B更新数据库将x更新为20

    请求A更新数据库将x更新为10

    此时数据中为20, Redis中为10 出现缓存和数据库数据不一致情况

  3. 先删除Redis再更新数据库

    这种情况会出现的问题:

    请求A先将字段x更新为10

    请求B查询x的值

    正常流程

    请求A删除Redis中x的值

    请求A更新数据中的值为10

    请求B查询Redis中没有值, 查询数据库

    请求B回写缓存x的值

    异常流程

    请求A删除Redis中x的值

    请求B查询Redis中没有值, 查询数据库但是此时请求A还未更新数据库或者是还没有commit

    请求B查询数据库, 回写缓存

    请求A更新数据库

    此时缓存中仍然是缓存的旧值, 数据库和缓存值不一致

  4. 先更新数据库再删除Redis

    这种情况会出现的问题:

    请求A先将字段x更新为10

    请求B查询x的值

    正常流程

    请求A更新数据库

    请求A删除Redis中的缓存值

    请求B查询数据库并回写缓存

    异常流程

    请求A更新数据库但未提交

    请求B读取的是旧值

    请求A删除缓存

为了解决先删除后修改数据库的异常情况

延时双删

public void updateData(Object obj, String id) {// 先删除缓存中的数据redisTemplate.opsForValue().getAndDelete(DATA_PR + id);// 再更新数据库testMapper.updateData(obj);// 再次删除缓存中的数据避免其他线程读取旧值并回写缓存// 需要在这里等待,等待的原因是如果另外的线程读取的线程还在回写的流程中旧值还未写到缓存中, 那么删除是没有意义的// 这里等待的时间就是大于等待其他线程将旧值写入缓存的时间try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}redisTemplate.opsForValue().getAndDelete(DATA_PR + id);}

通常我们会采用先更新数据库再删除Redis缓存, 但是依然会存在在更新期间读取到旧值的情况, 还会存在删除缓存失败问题, 此时可引入消息中间件将需要更改的数据推送到MQ, 再通过MQ去对Redis进行删除, 这样也不能保证强一致性, 只是一个比较折中的方案

引入中间件自动同步数据到Redis canal

如果我们的数据库事MySQL可以通过引入开源中间件canal对MySQL的binlog进行监听, 当数据库表发生变化时自动去将MySQL的变更写到我们的缓存中

未完待续…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/73767.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解 Linux 文件权限:从 ACL 到扩展属性,解剖底层技术细节与命令应用

Linux 以其强大而精密的文件权限和属性管理机制著称,这一体系不仅是系统安全的关键基石,还为灵活性和扩展性提供了坚实支撑。从传统的九位权限模型到访问控制列表(ACL)、扩展文件属性(Extended Attributes)…

剑指Offer35- - 链表

1. 题目描述 这题题意感觉说的不是很清楚,容易让人产生歧义!其实题意很简单,给你一个链表 head,你深拷贝它,然后返回即可,注意不能修改原链表 /* // Definition for a Node. class Node { public:int val;N…

C 语言常用关键字详解:static、const、volatile

C 语言常用关键字详解:static、const、volatile 文章目录 C 语言常用关键字详解:static、const、volatile1. static 关键字1.1 用于局部变量示例: 1.2 用于全局变量示例: 1.3 用于函数示例: 2. const 关键字2.1 用于局…

Centos7本地部署阿里Qwen2-7B模型

1.从hagging face下载模型 2.把下载的模型文件,放到/usr/local/Qwen2-7B目录下 3.创建虚拟环境,安装依赖 1.环境安装 sudo yum update -y sudo yum install -y python3 python3-pip git 2.创建虚拟环境并激活 python3 -m venv qwen2_env source qwen2_…

群晖监控套件通过ONVIF协议添加海康摄像头

1. 首先登录录像机 通道管理 找到每个摄像头的IP地址 2. 登录某个摄像头 配置 3. 添加用户名(注意不能是admin) 设置账户密码 用户类型选管理员 4. 群晖里面添加摄像头,自动搜索,添加刚刚那个IP的摄像头 5. 验证…

【C++】 —— 笔试刷题day_8

一、求最小公倍数 题目解析 题目很简单,给定两个数a和b求它们的最小公倍数。 算法思路 对于求两个数的最小公倍数问题,想必已经非常熟悉了; 在之前学校上课时,记得老师提起过,最小公倍数 两个数的乘积 除以最大公约数…

MTK Android12-Android13 设置系统默认语言

Android 系统,默认语言 文章目录 需求:场景 参考资料实现方案实现思路编译脚本熟悉-平台熟悉mssi_64_cnkernel-4.19 解决方案修改文件-实现方案 源码分析PRODUCT_LOCALES 引用PRODUCT_DEFAULT_LOCALE 定义get-default-product-locale 方法定义PRODUCT_DE…

系统如何查找文件?inode号又是什么?

下面分别详细解释您提到的三个问题: “文件系统怎么定位文件”、“inode 是什么”、“为什么删除后还可能被占用”。 一、文件系统怎么定位文件 1.1 目录与文件名并不直接存储文件数据 在常见的 Unix/Linux 文件系统(如 ext4、xfs)或类似的…

05-SpringBoot3入门-整合SpringMVC(配置静态资源、拦截器)

1、说明 在01-SpringBoot3入门-第一个项目-CSDN博客中,其实就已经整合了SpringMVC。下面讲解怎么配置静态资源和拦截器 2、配置静态资源 命名:static(文件夹) 位置:src/main/resources 编写一个html文件 访问 http:/…

Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量回归预测

聚划算!Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量回归预测 目录 聚划算!Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 聚划算!Tran…

树莓派浏览器配置全解析:从轻量系统到网页应用平台

树莓派(Raspberry Pi)不仅是嵌入式开发的入门利器,也因其低成本和强大的社区支持而成为物联网、数字标牌、教育培训等领域的热门平台。在很多应用中,运行一个浏览器并作为 Web 前端展示、操作或交互的能力显得尤为关键。 但在资源…

初识Qt(一)

本文部分ppt、视频截图原链接:萌马工作室的个人空间-萌马工作室个人主页-哔哩哔哩视频 1. Qt是什么? Qt是一个跨平台的C应用程序开发框架,它既为图形用户界面(GUI)程序开发提供了强大支持,也能用于开发非GUI的控制台程序、服务端…

六十天前端强化训练之第三十二天之Babel 转译配置大师级深度讲解

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗,谢谢大佬! 目录 一、核心概念与知识体系详解 1. Babel 工作原理全景解析 二、完整配置方案(带详细注释) 1. 进阶版 .babelrc 配置 2. Webpack 集成配置&#xff08…

智能提示词生成器:助力测试工程师快速设计高质量测试用例

在软件测试中,测试用例设计方法的选择和实施是确保软件质量的重要步骤。测试工程师经常需要根据不同的测试场景、参数维度和业务需求,设计出覆盖率高且有效的测试用例。然而,设计测试用例并非易事,特别是在面对复杂的业务逻辑时。 为了帮助测试工程师高效生成测试用例提示…

beanie.exceptions.CollectionWasNotInitialized

遇到这样的情况不要慌,不要慌 1:检查模型是否已经初始化: class TaskModel(Document):"""定时任务模型"""task_id: str Field(default_factorylambda: str(uuid.uuid4()), # 新增默认值description"任…

【CVE-2025-30208】| Vite-漏洞分析与复现

漏洞简介 CVE-2025-30208 是 Vite 开发服务器中的一个任意文件读取漏洞。该漏洞允许攻击者通过特定的 URL 参数绕过访问控制,从而读取服务器上的敏感文件(如 /etc/passwd 或 C:\windows\win.ini)。 该漏洞主要影响以下版本的 Vite&#xff…

将 Markdown 表格结构转换为Excel 文件

在数据管理和文档编写过程中,我们经常使用 Markdown 来记录表格数据。然而,Markdown 格式的表格在实际应用中不如 Excel 方便,特别是需要进一步处理数据时。因此,我们开发了一个使用 wxPython 的 GUI 工具,将 Markdown…

Golang使用 ip2region 查询IP的地区信息

利用 ip2region 进行 IP 地址定位 import ("fmt""log""github.com/lionsoul2014/ip2region/binding/golang/xdb" )func main() {ip : "213.118.179.98"dbPath : ".\\cmd\\ip\\ip2region.xdb"// 1、初始化查询器//searcher,…

对匿名认证的理解

概述:在 Spring Security 中,** 匿名认证(Anonymous Authentication)** 是一种特殊的认证机制,用于处理未提供有效凭证的请求。 匿名认证的本质 目的:允许未认证用户访问特定资源。原理: 当请求…

C++调用Python

Python安装 地址: python官网 可以根据需要下载对应的版本。 调用python python测试脚本 # my_script.py import sys import jsondef calculate(a, b):return a * b 10 # 示例计算逻辑if __name__ "__main__":# 从命令行参数读取 JSON 字符串try…