在开发中经常使用到redis。redis作为缓存可以加快程序响应速度。从redis缓存中读取数据大致需要0.5ms左右,从数据库需要几毫秒。大致业务流程如下图:

每次设置缓存时都有一个过期时间,根据不同业务,过期时间也不一致,设置过期时间能保证缓存数据最终一致性问题。这样能保证在更新数据库成功,更新缓存失败,或者缓存了脏数据时,缓存过期后,能正确的读取到最新的值。

我们常见的三种缓存更新方案:

  1. 先更新数据库,再更新缓存
  2. 先删除缓存,再更新数据库
  3. 先更新数据库,再删除缓存

方案一先更新数据库,再更新缓存
先更新数据库,再更新缓存,这么做的基本上很少很少。这种方案有以下缺点:

  1. 并发更新问题,比如线程A更新了数据库,线程B更新了数据库,线程B更新了缓存,线程A更新了缓存,这样最终存入的就是脏数据。
  2. 业务维护难度大,比如有些更新操作多,但是读取时并不多,可能浪费更新到redis的资源,另外redis缓存的数据并不一定是直接写入数据库的,可能是经过刷选,过滤,复杂计算得出的,这个时候维护麻烦,每次写入数据库,都得更新缓存,重复计算,刷选。并且不一定是更新一张表的数据要更新缓存,可能缓存跟多张表的数据有关系。

这里实际使用最多的是方案二和方案三

方案二先删除缓存,再更新数据库
这种方案在我们实际中使用较多,大部分都能容忍可能出现的脏数据的业务,及时出现脏数据,缓存过期后,也会读取最新的值。说下这种方案存在的问题

  1. 存在脏数据的可能,比如线程A删除缓存,线程B查询缓存不存在数据,从数据库获取,获取成功后,数据存入缓存,现在A更新数据。这样缓存中的数据就是脏数据了。

脏数据解决方案采用双删

       # 删除缓存redisConn.delete("cacheKey")# 更新数据库db.execute("update t set count = count +1 where id = 10")# 延时删除缓存sleep(1000)redisConn.delete("cacheKey")

这种方案有以下缺点:

  1. 多次操作redis删除key
  2. 延时删除,导致接口性能不高,影响接口吞吐量
  3. 第二次可能删除失败,还是存在问题

解决方案,异步删除时可以使用MQ消息队列(比如RocketMq的延时消息),确保删除成功,删除失败则重试,这种方案对业务代码影响大,造成大量的侵入,并且MQ也可能存在消息堆积,删除延迟过长的问题。

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

我司(核心接口每天请求量几千万级别,集群百万QPS)目前采用的是这种方案,先更新数据库,再删除缓存。这种方案虽然也会出现脏数据,但是概率极低,而且redis也有过期时间,能够保证最终一致性。

        # 更新数据库db.execute("update t set count = count +1 where id = 10")# 删除缓存redisConn.delete("cacheKey")

存在的问题

  1. 请求A查询数据库,得一个旧值,请求B将新值写入数据库,请求B删除缓存,请求A将查到的旧值写入缓存。这种情况下会存在脏数据。

出现这种问题的概率极低,除非是查询比写入慢。要解决也可以采用异步延时删除。说实话如果对于这种极低概率的脏数据都不能容忍,建议不需要使用缓存了。毕竟现在大部分都是读写分离,主从还存在延时呢。这种要强一致性的建议走mysql。对msql进行扩容比如分库分表,读写分离等等。

当然非得使用缓存又要保存数据强一致性,也有办法。采用消息队列异步删除,采用binlog同步缓存数据,删除缓存,不过这种方案代码侵入大,维护难,大部分都采用方案三。

缓存强一致性方案流程如下:

参考资料:redis缓存和数据库双写一致性问题

Redis缓存一致性问题解决方案相关推荐

  1. redis专题:数据库和redis缓存一致性解决方案

    文章目录 1.双写模式 2.失效模式 3.缓存一致性解决方案 redis缓存和数据库都保存了数据信息,当我们更新了数据库的数据时,应该如何保证redis和数据库的数据同步呢?当前比较常用的是双写模式和 ...

  2. Redis 缓存常见问题:缓存一致性的解决方案

    文章目录 先删除缓存,再更新数据库 延时双删 先更新数据库,再删除缓存 修改缓存过期时间 消息队列 Redis 缓存常见问题 :缓存雪崩,缓存击穿,缓存穿透,缓存预热 在之前的博客中,我介绍了Redi ...

  3. 缓存一致性问题解决方案(超全超易懂)

    文章目录 1.缓存模型和思路 2.缓存更新策略 3.两种解决方案 3.1.先删除缓存,再更新数据库 3.1.1延时双删(解决先删除缓存,再更新数据库产生的缓存不一致问题) 1.什么是延时双删 2.为什 ...

  4. 【redis】redis缓存穿透及解决方案|缓存穿透,缓存击穿,雪崩的理解

    |目录 缓存穿透 解决方案 布隆过滤 缓存空对象 缓存雪崩 解决方案 1.保证缓存层服务高可用性 2.依赖隔离组件为后端限流并降级 3.数据预热 4.做二级缓存,或者双缓存策略. 5.缓存永远不过期 ...

  5. springboot 缓存一致性常用解决方案

    前言 多级缓存在微服务的架构设计中可谓随处可见,多级缓存作为提升系统高并发的常规手段,在各类大中小型的系统设计中都有体现: 下图是一张简单的服务端多级缓存设计示意图,多级缓存的常用解决方案,像ehca ...

  6. 高并发下redis缓存穿透问题解决方案

    一.使用场景 我们在日常的开发中,经常会遇到查询数据列表的问题,有些数据是不经常变化的,如果想做一下优化,在提高查询的速度的同时减轻数据库的压力,那么redis缓存绝对是一个好的解决方案. 二.需求 ...

  7. 小工匠聊架构-Redis 缓存一致性设计

    文章目录 Pre 思路 Spring 注解使用:控制 Redis 缓存更新 一致性问题是如何产生的? 双更新模式:操作不合理,导致数据一致性问题 "后删缓存"能解决多数不一致 (C ...

  8. redis缓存失效及解决方案

    缓存失效及解决方案 2018年11月16日 09:38:11 隔壁阿源 阅读数 758更多 分类专栏: 架构 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处 ...

  9. Redis缓存雪崩与解决方案

    什么是缓存雪崩?服务器雪崩的场景与解决方案 --转自头条号 优知学院 什么是应用服务雪崩 雪崩问题 分布式系统都存在这样一个问题,由于网络的不稳定性,决定了任何一个服务的可用性都不是 100% 的.当 ...

最新文章

  1. C#“.NET研究”类类型
  2. 五种应该避免的代码注释
  3. no such file or directory AndroidManifest.xml
  4. python列表间隔合并_Python使用zip合并相邻列表项的方法示例
  5. fast_recovery_area无剩余空间(ORA-19815)
  6. Python已成美国顶尖高校中最受欢迎的入门编程语言
  7. android在activity中锁屏解锁后重走OnCreate的问题的解决办法
  8. TensorFlow CTC
  9. 自动化测试-selenium启动浏览器
  10. 《C++ Primer Plus》读书笔记之十—类和动态内存分配
  11. android单选控件spinner与数据库结合综合实例
  12. oj 小黑华丽的逆袭机会
  13. 在打开 Office XP 或 Office 2003 文档时,会提示您为 ActiveX 控件授予权限
  14. datedif函数(datedif函数在哪里找)
  15. python实现屏幕视频录制_用Python来做一个屏幕录制工具
  16. 【前端】HTML详细教程(下篇)
  17. VAE_MNIST数字图片识别及生成
  18. 在Android项目中使用SpringBoot框架
  19. 聊聊 C++ 和 C# 中的 lambda 玩法
  20. Oracle ERP 库存管理(业务流程 核心流程)

热门文章

  1. 再谈数据安全:TCG Opal介绍
  2. android uri图片压缩,详解android 通过uri获取bitmap图片并压缩
  3. 到底人工智能前景好不 首选哪个开发语言好
  4. C++统计文件夹中文件个数
  5. 多测师拱墅校区肖sir_高级金牌讲师_html讲解
  6. Long类型的数据转换
  7. 二次型如何快速转化为矩阵?
  8. 详解鸽巢原理【组合数学】
  9. unity3d 模拟电脑实现_基于Unity3D的机器人仿真实验系统
  10. 幼儿园买玩具_二进制枚举