Redis之缓存一致性

Redis之缓存一致性

  • 1 缓存更新策略
    • 1.1 内存淘汰
    • 1.2 过期删除
    • 1.3 主动更新
    • 1.4 三种缓存更新策略的对比
  • 2 更新缓存的两种方式
  • 3 缓存更新策略的实现方式
    • 3.1 先更新DB,后更新缓存
    • 3.2 先更新DB,后删除缓存
    • 3.3 先更新缓存,后更新DB
    • 3.4 先删除缓存,后更新DB
    • 3.5 延迟双删
    • 3.6 异步删除缓存
      • 3.6.1 基于消息队列的异步删除缓存
      • 3.6.2 基于MySQL的bin log+消息队列删除缓存
      • 3.6.3 异步删除缓存的优缺点
      • 3.6.4 基于 阿里canal实现
    • 3.7 几种实现方式的对比

1 缓存更新策略

按照缓存更新的方式大致分为: 内存淘汰、过期删除、主动更新。

1.1 内存淘汰

利用Redis的内存淘汰策略,当内存不足时自动进行淘汰部分数据,下次查询时更新缓存,一致性差,无维护成本。

因为Redis是基于内存的,如果内存超过限定值(Redis配置文件的maxmemory参数决定Redis最大内存使用量),导致新的数据存不进去,此时Redis会根据淘汰策略删除一些数据。

淘汰策略由Redis配置文件的maxmemory-policy参数决定设置,默认为noeviction模式。

淘汰策略的执行过程

  • 执行写请求时,Redis会检查内存使用情况,内存使用超过限定值,按照淘汰策略删除key
  • Redis写入新数据。

具体的淘汰策略redis.windows-service.conf中可以查到

  1. noeviction:默认策略,当写入新数据后的内存超过限定值时,写请求直接返回错误,只读请求可以正常执行。
  2. allkeys-lru:当写入新数据后的内存超过限定值时,从所有key中使用LRU算法(最近最少使用算法)淘汰最久没有使用过的key
  3. volatile-lru:当写入新数据后的内存超过限定值时,从设置了过期时间的key中使用LRU算法淘汰最久没有使用过的key
  4. allkeys-random:当写入新数据后的内存超过限定值时,从所有key中随机淘汰key
  5. volatile-random:当写入新数据后的内存超过限定值时,从设置了过期时间的key中随机淘汰key
  6. volatile-ttl:当写入新数据后的内存超过限定值时,从设置了过期时间的key中根据过期时间淘汰key,越快过期越早淘汰。
  7. allkeys-lfu:当写入新数据后的内存超过限定值时,从所有key中使用LFU算法(最少频率访问算法)淘汰使用频率最低的key
  8. volatile-lfu:当写入新数据后的内存超过限定值时,从设置了过期时间的key中使用LFU算法淘汰使用频率最低的key

1.2 过期删除

缓存添加过期时间,到期后根据过期删除策略自动进行删除缓存,下次查询时更新缓存,一致性一般,维护成本低。

  1. 定时删除key设置了过期时间,一旦过期立即删除。
    • 优点:key一旦过期就会立即删除,不会占用内存。
    • 缺点:过期key较多时,删除key会占用CPU时间,影响服务器的响应时间,吞吐量,性能。
  2. 惰性删除:过期key不会马上被删除,而是继续保存在内存中,当key被访问时检查key的过期时间,若已过期则删除。
    • 优点:只在访问时才会对检查key的过期时间,没使用的key不会占用CPU的时间去检查过期时间,不会影响服务器的响应时间,吞吐量,性能。
    • 缺点:没有被访问的过期key继续保存在内存中,导致内存不会被释放,消耗内存资源。
  3. 定期删除:每隔一段时间(时间可以自行设置,Redis配置文件的hz参数表示1s执行多少次定期删除策略,默认值10),随机抽取设置了过期时间的key检查它们的过期时间,删除已过期的key
    • 优点:可以指定频率来减少删除操作对CPU性能的影响,定期删除也能释放没有被访问的过期key占用的内存。
    • 缺点:频率高影响CPU的性能,频率低过期key占用的内存不会及时释放。

1.3 主动更新

应用程序中修改DB,修改缓存,一致性好,维护成本高。

主动更新大致分为: Cache Aside PatternRead/Write Through PatternWrite Behind Caching Pattern

  1. Cache Aside Pattern:即旁路缓存模式,旁路路由策略,最经典常用的缓存策略。由应用程序负责缓存和DB的读写。读写操作步骤:
    • 读操作时,先读缓存,缓存存在直接返回;缓存不存在则读DB,然后把读的DB数据存入缓存,返回。
    • 写操作时,先更新DB,再删除缓存。
  2. Read/Write Through Pattern:即读写穿透模式,该模式下应用程序只与缓存管理组件交互,缓存管理组件负责缓存和DB的读写。
    • Read Through:读操作时,缓存管理组件先读缓存,缓存存在直接返回;缓存不存在则读DB,然后把读的DB数据存入缓存,返回。
    • Write Through:写操作时,缓存管理组件同步更数DB和缓存。
  3. Write Behind Caching Pattern:即异步缓存写入,该模式下应用程序只与缓存管理组件交互操作,缓存管理组件负责缓存和DB的读写,通过定时或阈值的异步方式将数据同步到DB,保证最终一致。该模式和Read/Write模式相似,不同点在于Read/Write模式更新DB和更新缓存是同步的,而Write Behind Caching Pattern模式更新DB和更新缓存是异步的。
    • 优点:减少了更新DB的频率,读写响应非常快,吞吐量也会有明显的提升。
    • 缺点:不能实时同步,数据同步DB过程服务不可用,导致数据丢失。

三种主动更新策略的对比

策略说明优点缺点
Cache Aside Pattern应用程序负责缓存和DB的读写使用简单,直接操作缓存和DB需要编写对缓存和DB读写的代码
Read/Write Through Pattern应用程序只与缓存管理组件交互,缓存管理组件负责缓存和DB的读写使代码更简洁缓存管理组件需要提供对DB和缓存读写的方法
Write Behind Caching Pattern应用程序只与缓存管理组件交互,缓存管理组件负责缓存和DB的读写性能最好,在高并发场景下可以降低数据库的压力缓存管理组件,需要提供对DB和缓存读写的方法;
不能实时同步,数据同步DB过程DB不可用,导致数据丢失;
一致性不强,对一致性要求高的系统不适用

1.4 三种缓存更新策略的对比

策略说明一致性维护成本
内存淘汰使用Redis的内存淘汰策略,当内存不足时自动进行淘汰部分数据,下次查询时更新缓存
过期删除缓存添加过期时间,到期后根据过期删除策略自动进行删除缓存,下次查询时进行更新缓存
主动更新修改数据库时也修改缓存,使用硬编码方式或者硬编码+中间件方式在修改数据库时同步或异步的修改缓存

2 更新缓存的两种方式

  1. 删除缓存:更新DB时删除缓存,查询时再从DB中读取数据并更新到缓存。
  2. 更新缓存:更新DB时更新缓存,频繁更新缓存开销大,且并发时可能导致请求读取的缓存数据是旧数据。

3 缓存更新策略的实现方式

3.1 先更新DB,后更新缓存

1 并发写场景
所有线程都是先更新DB再更新缓存,在某个写线程更新DB后继续更新缓存时,可能因为网络原因出现延迟,这时其他写线程也更新了DB和缓存,导致缓存和DB数据不一致。

具体步骤:

  1. 线程1更新DB
  2. 线程2更新DB
  3. 线程2更新缓存
  4. 线程1更新缓存

总结
理论上先更新DB的线程理应也会先更新缓存,但是并发场景下线程的执行顺序无法保证:

  • 若更新缓存的顺序是: 先线程1再线程2,则不会出现数据不一致问题。
  • 若更新缓存的顺序是: 先线程2再线程1,此时缓存是线程1的数据,DB是线程2的数据,导致缓存和DB数据不一致。

2 并发读写场景
在写线程更新DB和更新缓存之间,读线程可以获取到旧数据,但最终会一致。
具体步骤:

  1. 线程1更新DB
  2. 线程2查询,命中缓存返回
  3. 线程1更新缓存

总结
线程2获取的缓存是旧数据,但最终都会一致。

3.2 先更新DB,后删除缓存

1 并发写场景
所有线程都是先更新DB再删除缓存,无论哪个线程先更新DB再删除缓存,缓存都会被删除,不会导致缓存和DB数据不一致。

具体步骤:

  1. 线程1更新DB
  2. 线程2更新DB
  3. 线程2删除缓存
  4. 线程1删除缓存

总结
无论哪个线程先更新DB再删除缓存,缓存都会被删除,不会导致缓存和DB数据不一致。


2 并发读写场景
在写线程更新DB再删除缓存之间,读线程可以获取到旧数据,但最终会一致。

具体步骤:

  1. 线程1更新DB
  2. 线程2查询命中缓存返回
  3. 线程1删除缓存

总结
线程2获取的缓存是旧数据,但后续最终都会一致。

3.3 先更新缓存,后更新DB

1 并发写场景
所有线程都是先更新缓存再更新DB,在某个写线程更新缓存和更新DB之间,其他写线程也更新了缓存和DB,导致缓存和DB数据不一致。

具体步骤:

  1. 线程1更新缓存
  2. 线程2更新缓存
  3. 线程2更新DB
  4. 线程1更新DB

总结:理论上先更新缓存的线程也会先更新DB,但是并发场景下线程的执行顺序无法保证:

  • 若更新DB的顺序是: 线程1再线程2,则不会出现数据不一致问题。
  • 若更新DB的顺序是: 线程2再线程1,此时缓存是线程2的数据,DB是线程1的数据,导致缓存和DB数据不一致。

2 并发读写场景
在写线程更新缓存和更新DB之间,读线程也可以获取到最新的缓存,不会导致缓存和DB数据不一致。

具体步骤:

  1. 线程1更新缓存
  2. 线程2查询,命中缓存返回
  3. 线程1更新DB

总结
可以保证缓存和DB数据一致,虽然线程1更新DB的操作还没有完成,但是更新缓存的操作已经完成了,读请求可以获取到最新的缓存。

3.4 先删除缓存,后更新DB

1 并发写场景
所有线程都是先删除缓存再更新DB,无论哪个线程先删除缓存再更新DB,缓存都会被删除,不会导致缓存和DB数据不一致。

具体步骤:

  1. 线程1删除缓存
  2. 线程2删除缓存
  3. 线程2更新DB
  4. 线程1更新DB

总结
无论哪个线程先删除缓存再更新DB,缓存都会被删除,不会导致缓存和DB数据不一致。


2 并发读写场景
在写线程删除缓存和更新DB之间,读线程根据查询的DB结果更新了缓存,导致缓存和DB数据不一致。

具体步骤:

  1. 线程1删除缓存
  2. 线程2查询,未命中
  3. 线程2查询DB
  4. 线程2根据查询的DB结果更新缓存
  5. 线程1更新DB

总结:
线程1删除缓存和更新DB之间,线程2根据查询的DB结果更新了缓存,导致缓存和DB数据不一致。

3.5 延迟双删

因为3.4 先删除缓存,再更新DB,在并发读写场景会导致数据不一致。
延迟双删是基于先删除缓存再更新DB的基础上的改进,在更新DB后延迟一定时间,再次删除缓存。

延迟是为了保证第二次删除缓存前能完成更新DB操作,延迟时间根据系统的查询性能而定。
第二次删除缓存是为了保证后续请求查询DB(此时数据库中的数据已是更新后的数据),重新写入缓存,保证数据一致性。

1 并发写场景
无论哪个线程都会删除缓存,所以不会导致缓存和DB数据不一致。

具体步骤:

  1. 线程1删除缓存
  2. 线程2删除缓存
  3. 线程2更新DB
  4. 线程1更新DB
  5. 线程1延时删除缓存
  6. 线程2延时删除缓存

2 并发读写场景
具体步骤:

  1. 线程1删除缓存
  2. 线程2查询,未命中
  3. 线程2查询DB
  4. 线程2根据查询的DB结果更新缓存
  5. 线程1更新DB
  6. 线程1延时删除缓存

总结:
线程1第一次删除缓存之后,线程2根据查询的DB结果更新缓存,此时查询得到的结果是旧数据,线程1延迟第二次删除缓存之后,后续查询DB(此时数据库中的数据已是更新后的数据),重新写入缓存,不会导致缓存和DB数据不一致。


3 延时双删的缺点:

  1. 需要延时,低延时场景不合适,如秒杀等需要低延时,需要强一致,高频繁修改数据场景。
  2. 不能保证强一致性,在更新DB之前,查询线程查询得到的结果是旧数据,可但可以减轻缓存和DB数据不一致的问题。
  3. 延时的时间是一个不可评估的值,延时越久,能规避一致性的概率越大。

3.6 异步删除缓存

因为3.2 先更新DB,后删除缓存 在并发写场景不会导致数据不一致,但是在并发读写场景会短暂的导致数据不一致,但是由于删除缓存失败不会重试,并发写场景、并发读写场景都可能长时间导致数据不一致。

异步删除缓存是对先更新DB,后删除缓存的改进:更新DB之后,基于消费队列异步删除缓存。

根据消费队列不同大致分为:消息队列、bin log+消息队列。

3.6.1 基于消息队列的异步删除缓存

1 并发写场景
无论哪个线程先更新DB再删除缓存,缓存都会被删除,不会导致缓存和DB数据不一致。

具体步骤:

  1. 线程1更新DB
  2. 线程2更新DB
  3. 线程2把删除缓存放入消息队列
  4. 线程1把删除缓存放入消息队列
  5. 异步:消息队列消费删除缓存

总结:
无论哪个线程先更新DB再删除缓存,缓存都会被删除,不会导致缓存和DB数据不一致。

2 并发读写场景
异步删除缓存期间,读线程获取的缓存是旧数据,短暂出现数据不一致,异步删除缓存后最终会一致。

具体步骤:

  1. 线程1更新DB
  2. 线程2查询缓存,命中返回
  3. 线程1把删除缓存放入消息队列
  4. 异步:消息队列消费删除缓存

总结:
异步删除缓存期间,读线程获取的缓存是旧数据,短暂出现数据不一致,异步删除缓存后最终会一致。

3.6.2 基于MySQL的bin log+消息队列删除缓存

1 并发写场景

具体步骤:

  1. 线程1更新DB
  2. 线程2更新DB
  3. 异步:bin log日志收集中间件定时收集DBbin log日志
  4. 异步:bin log日志收集中间件发送日志消息到消息队列
  5. 异步:消息队列消费删除缓存

总结:
无论哪个线程先更新DB再删除缓存,缓存都会被删除,不会导致缓存和DB数据不一致。


2 并发读写场景
具体步骤:

  1. 线程1更新DB
  2. 线程2查询缓存,命中返回
  3. 异步:bin log日志收集中间件定时收集DBbin log日志
  4. 异步:bin log日志收集中间件发送日志消息到消息队列
  5. 异步:消息队列消费删除缓存

总结:
异步删除缓存期间,读线程获取的缓存是旧数据,短暂出现数据不一致,异步删除缓存后最终会一致。

3.6.3 异步删除缓存的优缺点

优点:

  1. 删除缓存的操作与主流程代码解耦。
  2. 中间件自带重试机制,增加了操作缓存的成功率。

缺点:
引入中间件,提升了系统的复杂度,在高并发场景可能会产生性能问题。

3.6.4 基于 阿里canal实现

canal是阿里开发的基于数据库增量日志解析,提供增量数据的订阅和消费,目前主要支持MySQLbin log解析。基于canal的实现方案完全避免了对业务代码的侵入,核心业务代码只管更新数据库,其他的不用care

canal地址:https://github.com/alibaba/canal

MySQL会将操作记录在bin log日志中,通过canal去监听数据库日志二进制文件,解析bin log日志,同步到Redis中进行增删改操作。

canal的工作原理:canal是模拟MySQL slave的交互协议,伪装自己为MySQL slave,向MySQL master发送dump协议;MySQL master收到dump请求,开始推送bin logslave (即canal);canal解析bin log象(原始为byte流)。
在这里插入图片描述

3.7 几种实现方式的对比

参看:redis之缓存一致性 最后一部分

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

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

相关文章

二叉树的最近公共祖先

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻强烈推荐优质专栏: 🍔🍟🌯C的世界(持续更新中) 🐻推荐专栏1: 🍔🍟🌯C语言初阶 🐻推荐专栏2: 🍔…

如何通过三行配置解决在Kubernetes中的gRPC扩展问题

一切都始于我向我们的高级软件工程师提出的一个问题: “忘掉通信速度。你真的觉得在gRPC中开发通信比REST更好吗?” 我不想听到的答案立刻就来了:“绝对是的。” 在我提出这个问题之前,我一直在监控我们的服务在滚动更新和扩展Po…

【k8s】ingress-nginx通过header路由到不同后端

K8S中ingress-nginx通过header路由到不同后端 背景 公司使用ingress-nginx作为网关的项目,需要在相同域名、uri,根据header将请求转发到不同的后端中在稳定发布的情况下,ingress-nginx是没有语法直接支持根据header做转发的。但是这个可以利…

【ARM Coresight 系列文章19 -- Performance Monitoring Unit(性能监测单元)

文章目录 1.1 PMU 介绍1.2 PMU 寄存器1.2.1 PMU 管理寄存器1.2.2 PMU 外设识别寄存器1.2.3 PMU 组件识别寄存器1.3 性能监控事件1.3.1 Cortex-A9 特定事件1.1 PMU 介绍 许多体系结构都包含 PMU(Performance Monitoring Unit)硬件,用于跟踪、计数系统内部的一些底层硬件事件…

[代码随想录]二叉树篇

文章目录 1. 二叉树之层序遍历1.1 144-二叉树的前序遍历1.2 94-二叉树的中序遍历1.3 145-二叉树的后序遍历1.4 102-二叉树的层序遍历1.5 107-二叉树的层序遍历II1.6 199-二叉树的右视图1.7* 637-二叉树的层平均值1.8* 429-N叉树的层序遍历1.9 515-在每个树行中找最大值1.10* 11…

k8s修改集群IP--重置集群

原来IP地址 192.168.10.138 k8s-master 192.168.10.139 k8s-node1 192.168.10.140 k8s-node2 新IP地址 192.168.10.148 k8s-master 192.168.10.149 k8s-node1 192.168.10.150 k8s-node2 cp -Rf /etc/kubernetes/ /etc/kubernetes-bak pki 证书目录保留下来: rm -rf …

Ubuntu18.04下载安装基于使用QT的pcl1.13+vtk8.2,以及卸载

一、QVTKWidget、QVTKWidget2、QVTKOpenGLWidget、QVTKOpenGLNativeWidget 区别 1.Qt版本 Qt5.4以前版本:QVTKWidget2/QVTKWidget。 Qt5.4以后版本:QVTKOpenGLWidget/QVTKOpenGLWidget。 2.VTK版本(Qt版本为5.4之后) 在VTK8.2以前的版本:QVT…

Springboot——集成jodconverter做文档转换

文章目录 前言jodconverter 简介下载安装 libreoffice代码演示1、创建springboot项目工程并引入依赖2、配置3、准备一个docx模板4、编写测试代码 运行后的样式linux 环境下安装 libreoffice 前言 公司项目开发中,早期使用docx4j进行word转pdf,出现了很多…

网络安全工程师最详细学习和职业规划路线(书籍推荐和导图下载)

网络安全行业热火朝天,但我们很少看到这个领域相关职业路线的规划,这一方面是由于这个行业还比较年轻,还没有完全建立职业路径,另一方面也是因为高端职位以前比较少,很少有人到达顶峰,所以难以总结。 但随…

计算机毕业设计 it职业生涯规划系统的设计与实现 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…

动手学强化学习第2章多臂老虎机

2.1简介 多臂老虎机问题可以被看作简化版的强化学习问题。但是其只有动作和奖励没有状态信息,算是简化版的强化学习问题。 2.2问题介绍 2.2.1问题定义 在多臂老虎机(MAB)问题中,有一个有K根拉杆的老虎机,拉动每一根拉杆都对应一个关于奖励…

在 centos7 上安装Docker

1、检查linux内核 Docker 运行在 CentOS 7 上,要求系统为64位、系统内核版本为 3.10 以上。 Docker 运行在 CentOS-6.5 或更高的版本的 CentOS 上,要求系统为64位、系统内核版本为 2.6.32-431 或者更高版本。 uname -r 2、使用 root 权限登录 Centos…

docker应用记录总结

一、前言 docker这类部署工具,久而久之不使用非常容易忘记,甚至连操作命令都容易忘记。网上也有比较全的docker使用教程。这里做一个记录总结,纯属是温故知新。 二、docker部署应用 1、docker印象 docker首先让我想到的是是虚拟化技术&…

踩雷react-useRef钩子函数

今天测试提了一个bug,之前做的有个需求,在触发事件发起请求后,成功响应返回的新的数据没有第一时间渲染到网页上。 方法也都成功更新了数据,就是渲染会慢1-2分钟,排错排了老半天,最后找到了原因。 一般情…

SpringBoot和Hibernate——如何提高数据库性能

摘要:本文由葡萄城技术团队发布。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 前言 在软件开发领域,性能是重中之重。无论您是构建小型 Web 应用程序还是大型企业系统…

单目标应用:遗传算法(Genetic Algorithm,GA)求解微电网优化MATLAB

一、微网系统运行优化模型 微电网优化模型介绍: 微电网多目标优化调度模型简介_IT猿手的博客-CSDN博客 二、遗传算法GA 遗传算法(Genetic Algorithm,GA)起源于对生物系统所进行的计算机模拟研究,是一种随机全局搜索…

【Java 进阶篇】JavaScript 数据类型详解

JavaScript是一种弱类型脚本语言,具有动态类型。这意味着JavaScript中的变量可以容纳不同类型的数据,并且它们的类型可以在运行时更改。在本文中,我们将深入探讨JavaScript中的数据类型,包括原始数据类型和引用数据类型&#xff0…

安科瑞ARB5系列弧光保护装置,智能电弧光保护,保障用电安全

安科瑞虞佳豪壹捌柒陆壹伍玖玖零玖叁 什么是弧光 电弧是放电过程中发生的一种现象,当两点之间的电压超过其工频绝缘强度极限时就会发生。当适当的条件出现时,一个携带着电流的等离子产生,直到电源侧的保护设备断开才会消失。空气在通常条件…

【ARM CoreLink 系列 6 -- DMC-400控制器简介】

文章目录 1.1 DMC-400 简介1.1.1 DFI(DDR PHY Interface)1.1.2 DFI 接口组1.1.3 DMC-400 兼容协议1.1.4 DMC-400 特性1.1.5 DMC-400 Interface 1.1 DMC-400 简介 DMC-400是一个由ARM开发、测试和授权的动态内存控制器,同时 DMC-400也是一个符…

git 回滚到指定版本

第一步:找到指定的需要回滚的版本的版本号 项目终端输入命令git log --oneline 第二步:使用git命令回滚到指定的版本 git reset --hard 版本号 第三步:此时再推到远程仓库用git push 会报错,需要用git push -f强推上去才可以哦