当前位置: 首页 > article >正文

如何保证Redis缓存和数据库一致性?

想要保证缓存与数据库的双写一致,一共有4种方式:
先更新缓存,再更新数据库;
先更新数据库,再更新缓存;
先删除缓存,再更新数据库;
先更新数据库,再删除缓存。

我们需要做的是根据不同的场景来使用合理的方式来解决数据问题。

第一种:先删除缓存,再更新数据库
在出现失败时可能出现的问题:

  1. 线程A删除缓存成功,线程A更新数据库失败;
  2. 线程B从缓存中读取数据;由于缓存被删,进程B无法从缓存中得到数据,进而从数据库读取数据;此时数据库中的数据更新失败,线程B从数据库成功获取旧的数据,然后将数据更新到了缓存。
    最终,缓存和数据库的数据是一致的,但仍然是旧的数据。

第二种:先更新数据库,再删除缓存
假设这会有两个请求,一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生

  1. 缓存刚好失效
  2. 请求A查询数据库,得一个旧值
  3. 请求B将新值写入数据库
  4. 请求B删除缓存
  5. 请求A将查到的旧值写入缓存
    如果发生上述情况,确实是会发生脏数据。
    然而,发生这种情况的概率又有多少呢?
    发生上述情况有一个先天性条件,就是步骤3的写数据库操作比步骤2的读数据库操作耗时更短,才有可能使得步骤4先于步骤5
    数据库的读操作的速度远快于写操作的(不然做读写分离干嘛,做读写分离的意义就是因为读操作比较快,耗资源少),因此步骤3耗时比步骤2更短,这一情形很难出现。
    先更新数据库,再删缓存依然会有问题,不过,问题出现的可能性会因为上面说的原因,变得比较低。

第三种:给所有的缓存一个失效期
第三种方案可以说是一个大杀器,任何不一致,都可以靠失效期解决,失效期越短,数据一致性越高。但是失效期越短,查数据库就会越频繁。因此失效期应该根据业务来定。

  1. 并发不高的情况:
    读: 读redis->没有,读mysql->把mysql数据写回redis,有的话直接从redis中取;
    写: 写mysql->成功,再写redis;
  2. 并发高的情况:
    读: 读redis->没有,读mysql->把mysql数据写回redis,有的话直接从redis中取;
    写:异步话,先写入redis的缓存,就直接返回;定期或特定动作将数据保存到mysql,可以做到多次更新,一次保存;

第四种:加锁,使线程顺序执行
如果一个服务部署到了多个机器,就变成了分布式锁,或者是分布式队列按顺序去操作数据库或者 Redis,带来的副作用就是:数据库本来是并发的,现在变成串行的了,加锁或者排队执行的方案降低了系统性能,所以这个方案看起来不太可行。

第五种:采用双删
先删除缓存,再更新数据库,当更新数据后休眠一段时间再删除一次缓存。

方案推荐两种:

  1. 项目整合quartz等定时任务框架,去实现延时3–5s再去执行最后一步任务 。(推荐使用)
  2. 创建线程池,线程池中拿一个线程,线程体中延时3-5s再去执行最后一步任务(不能忘了启动线程)

第六种:异步更新缓存(基于订阅binlog的同步机制)
MySQL binlog增量订阅消费+消息队列+增量数据更新到redis读Redis
热数据基本都在Redis写MySQL:增删改都是操作MySQL更新Redis数据:MySQ的数据操作binlog,来更新到Redis:
1)数据操作主要分为两大块:一个是全量(将全部数据一次写入到redis)一个是增量(实时更新)。
这里说的是增量,指的是mysql的update、insert、delate变更数据。
2)读取binlog后分析 ,利用消息队列,推送更新各台的redis缓存数据。
这样一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。
其实这种机制,很类似MySQL的主从备份机制,因为MySQL的主备也是通过binlog来实现的数据一致性。
这里可以结合使用canal(阿里的一款开源框架),通过该框架可以对MySQL的binlog进行订阅,而canal正是模仿了mysql的slave数据库的备份请求,使得Redis的数据更新达到了相同的效果。
当然,这里的消息推送工具你也可以采用别的第三方:kafka、rabbitMQ等来实现推送更新Redis。

以上就是redis和数据库数据保持一致的方案。


http://www.kler.cn/a/194.html

相关文章:

  • SpringBoot环境和Maven配置
  • 协方差矩阵
  • 行情系统用什么数据库好
  • 2025新春烟花代码(二)HTML5实现孔明灯和烟花效果
  • iOS - AutoreleasePool
  • 3D机器视觉的类型、应用和未来趋势
  • 外卖点餐系统小程序 PHP+UniAPP
  • 如何通过C++ 将数据写入 Excel 工作表
  • 刷题专练之链表(一)
  • SQL执行过程详解
  • 教你成为比卡卡西还牛逼的全能忍者,全拷贝与分割函数
  • 蓝桥杯C++组怒刷50道真题
  • 金三银四、金九银十 面试宝典 Spring、MyBatis、SpringMVC面试题 超级无敌全的面试题汇总(超万字的面试题,让你的SSM框架无可挑剔)
  • 【JavaScript 逆向】百度旋转验证码逆向分析
  • 大数据分析案例-基于决策树算法预测ICU患者是否需要插管
  • 进程间通信IPC
  • 两年外包生涯做完,感觉自己废了一半....
  • 1. lua入门
  • 被骗进一个很隐蔽的外包公司,入职一个月才发现,已经有了社保记录,简历污了,以后面试有影响吗?...
  • iOS 语言基础初探 Xcode 工具
  • 步进电机运动八大算法
  • 什么是双亲委派模型?双亲委派模型有何作用?
  • Fabric系列 - 多通道技术(Muti-channel)
  • 【C语言蓝桥杯每日一题】—— 单词分析
  • 卷王都在偷偷准备金三银四了...
  • 基于SpringCloud的微服务架构学习笔记(4)http客户端Feign和网关GateWay