Redis学习二

embedded/2025/3/31 11:17:41/

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.ppmy.cn/embedded/177302.html

相关文章

reactor网络模型

一、介绍 1.为什么需要reactor网络模型 1.1 高并发支持 非阻塞I/O:Reactor模型通过非阻塞I/O操作,允许单线程处理多个连接,减少线程切换开销,提升并发能力。 事件驱动:基于事件驱动机制,系统只在有事件发…

【2025】基于springboot+vue的医院在线问诊系统设计与实现(源码、万字文档、图文修改、调试答疑)

基于Spring Boot Vue的医院在线问诊系统设计与实现功能结构图如下: 课题背景 随着互联网技术的飞速发展和人们生活水平的不断提高,传统医疗模式面临着诸多挑战,如患者就医排队时间长、医疗资源分配不均、医生工作压力大等。同时,…

【2025】基于ssm+jsp的二手商城系统设计与实现(源码、万字文档、图文修改、调试答疑)

基于SSMJSP的二手商城系统设计与实现系统功能结构图: 课题背景 随着经济的发展和人们生活水平的提高,二手交易市场日益活跃。人们对于闲置物品的处理方式逐渐从传统的废品回收转变为通过二手交易平台进行再利用。这种交易模式不仅能够帮助用户节省开支&a…

rabbitmq + minio +python 上传文件

功能实现 RabbitMq接收hello里面传来的消息根据消息在 MobileFile里面新建文件新建文件上传到minio python 新建文件 import os path ./MobileFile file_path os.path.join(path,"new_file.txt") with open(file_path, "w") as file: pass转换成…

RabbitMQ 快速入门

目录 为什么有 RabbitMQ?QueueExchange(消息分发策略)DirectTopicFanoutHeaders 常见的队列类型死信队列 (Dead Letter Queue,DLQ)应用场景定时任务监控与告警消费者拒绝(NACK/Reject&#xff0…

LLM - CentOS上离线部署Ollama+Qwen2.5-coder模型完全指南

文章目录 离线安装OllamaOllama下载Ollama硬件需求Ollama 常用命令参考Ollama安装Ollama 服务管理&开机启动开启局域网访问 Ollama 服务 离线安装模型gguf 文件格式下载Qwen2.5-Coder-7B-Instruct-GGUF格式选择 ( gguf 版本 )构建Modelfile文件加载并运行离线模型测试 集成…

当 0 编程基础,用 ChatGPT 和 Cursor 开发同一应用时… |AI 开发初体验

求人不如求己。 事情是这样的,前段时间,我看了本书,书里介绍了款应用,能计算财富自由价格,还能制定退休计划。 结果,我迫不及待去下载这个应用时,发现这应用功能残缺,完全不可用。 …

Stable Diffusion 基础模型结构超级详解!

1. Transformer 第一个只用 Attention 机制来解决序列到序列问题的模型,最早被 Google 用来解决翻译问题 对于中英翻译而言,需要解决三个具体的问题: 如何用数字表示中文和英文 如何让神经网络理解语义 如何让神经网络生成英文 1.1 Tok…