【面试资料】Redis篇 之 双写一致、持久化、数据过期策略 / 淘汰策略

一. 双写一致

1. 定义(关键词:缓存同步更新)

执行写操作时,修改DB数据的同时,Redis中的数据也要更新。

(补充1)双写一致业务场景:
Redis做“只读缓存”,DB先修改,缓存需要同步更新

(补充2)若Redis做“读写缓存”:
若缓存数据可以进行“增删改”,则需要指定“写回策略”。
→ 同步直写策略:写缓存时,也同步写DB;
→ 异步写回策略:写缓存时,不同步写DB,等到缓存数据淘汰时,再写回DB,有效减少写回次数。(但若出现缓存宕机,最新数据就会丢失。)

2. 读写操作 过程详解

  • 读操作:负责读取数据、更新缓存
    “先缓存,后DB;未缓存,再DB”,并触发Redis数据更新
    在处理好缓存的穿透、击穿、雪崩之后,读数据操作基本不会出现问题。
    在这里插入图片描述
  • 写操作:负责删除缓存、更新DB
    常见错误步骤一:简单的“先删除缓存,再修改DB”
    情况举例写线程1率先执行。若在缓存已删除DB未修改时,读线程2趁机执行,则会触发 “缓存未命中→读取DB数据(旧)→缓存更新(旧)” ,在此之后,写线程1才完成DB修改。
    导致后果:
    读取的是旧数据,缓存中也是旧数据,唯有数据库是新数据。
    在这里插入图片描述
    常见错误步骤二:简单的“先修改DB,再删除缓存”
    情况举例读线程1率先执行,且刚好缓存过期。若在数据已读取(旧DB数据)缓存未更新时,写线程2趁机执行,则会触发“DB更新→删除缓存(空删)”,在此之后,读线程1才完成缓存更新,并且是旧数据
    导致后果
    读取的是旧数据,缓存中也是旧数据,唯有数据库是新数据。
    在这里插入图片描述
    写操作错误总结
    以上两种错误的原因皆为 “读写线程交织” 导致 “读写不协同”;简单来说,就是写线程的“删除缓存”总是在读线程“更新缓存”之前进行,导致缓存最终滞留脏数据
    反言之,若能①妥善处理脏数据再次进入,或②读写线程不发生重叠,又或③能够保证读写协同,则不会出现不一致错误。

综上,通过对读写过程的分析,我们得出结论:搭载“只读缓存”的系统在执行写操作时,必须采用特定方案解决双写一致问题,同步缓存与DB数据。

2. 解决方案

(1)延时双删

问题一:为何双删?
写操作两个示例的后果可以看出,最终都是 “旧缓存+新DB” 的情况。并且以上我们总结过,“读写交织”本不是错,但是极易出现 “删完又进脏数据” 的情况。

所以,我们在写操作完成DB修改后,再次删除缓存避免缓存中滞留脏数据

下一次读操作的时候,线程会向DB读取最新数据,并完成缓存更新

问题二:为何延时?
写操作的 “常见错误步骤一” 举例:线程具有 “独立运行、状态不可预测” 的特点,所以我们无法确认,读线程2(B事务)的缓存更新一定早于写线程1(A事务)的DB更新
如果 写线程1执行“立即双删” 策略,则无法确保脏数据进入缓存。
在这里插入图片描述
所以,延时的作用:等待读取到旧DB数据的线程全部结束脏数据已经全部进入缓存,再执行删除。
在这里插入图片描述
① 优势:简单。
② 劣势:延时的时间无法精确把控时间过短可能仍有“带旧数据的读线程”未结束,功亏一篑;时间过长会导致大量新一批的读线程(C、D事务)从缓存中获取到脏数据。(延时双删的天然痛点,无法解决!该方法实际不予使用。)

(2)普通互斥锁

暴力的将读写分离。读写操作不存在重叠,自然不需要考虑双写一致性问题,但是系统效率将大幅下降。所以实际不会使用此方法。
在这里插入图片描述

(3)Redission读写锁(强一致)

读线程加“读锁readLock”,也叫共享锁:允许其他读线程,阻止任何写线程。(可读、不可写)
写线程加“写锁writeLock”,也叫独占锁:阻止任何读写线程。(不可读、不可写)

① 优势:允许“读线程”并发,在保证读写分离强一致的前提下,最大限度保障系统性能。
② 劣势:写操作仍然会阻塞所有线程,影响系统性能。

(4)异步通知(弱一致)

异步通知 将不同的模块 归于 不同的服务,各服务在MQ的作用下协同运行,数据的最终一致性则依赖MQ的可靠性

所以,异步通知在确保一致性(MQ可靠)的前提下,仍可获得优秀的性能(无锁),不过会稍有延迟(消息传输耗时)。

  • 基于MQ:写操作更新DB数据 → 发送消息至MQ → 缓存服务持续监听MQ → 通知缓存删除(下次读操作更新)。
    在这里插入图片描述
  • 基于阿里Canal中间件:Canal伪装为MySQL的从节点,读取日志文件binlog,达到监听DB数据修改的效果;监听成功后,通知缓存更新。
    基于Canal的方法无需修改业务代码,是目前较推荐的双写一致解决策略
    在这里插入图片描述

① 优势:确保双写一致,极大的保障系统性能。
② 劣势:在大并发情况下会出现短暂延迟(大多数系统是可接受的)。

二. 持久化

1. 相关概念(关键词:缓存写入磁盘)

(1)为什么要持久化

Redis是基于内存的数据库。

  • 优点:cpu读取内存速度快,一秒钟可以进行数十万次,可以直接和cpu速度相近,读取极快
  • 缺点:断电数据丢失

为了防止其数据断电丢失,就需要将数据存入硬盘中,这样在断电后也可以访问到数据库当中的数据。

(2)持久化定义

以上将内存的数据写入到磁盘中,防止服务器宕机内存数据丢失,就是Redis的持久化。

(3)持久化方式

Redis支持两种持久化方式:RDB和AOF

2. RDB(内存快照)

(1)简介

Redis Database Backup file,Redis数据备份文件

RDB原理:按照一定的时间内存中所有数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。Redis实例重启后,从磁盘读取快照文件,恢复数据。

RDB持久化方式Redis是 默认开启 的,有多种触发的方式

(2)主动备份(客户端命令方式)

save:Redis 主进程 执行RDB,会阻塞所有命令。
bgsave:开启 子进程 执行RDB,避免主进程受到影响。(推荐)
在这里插入图片描述

(3)内部触发(Redis服务端方式)

配置redis.conf文件:管理员在redis.conf中设置save配置选项,Redis会在save选项条件满足之后自动触发一次BGSAVE命令,如果管理员设置了多个save配置选项,当 任意 save条件被满足,Redis都会触发一次BGSAVE命令。
在这里插入图片描述
shutdown:当Redis 通过shutdown指令接受到关闭服务器的请求时,会触发一次SAVE命令,阻塞所有的客户端,不再执行客户端发送的任何命令,在SAVE命令执行完毕后关闭服务器

3. AOF(命令记录)

(1)简介

Append Only File,日志追加文件

AOF原理:将Redis执行过的所有写命令记录到日志文件AOF末尾。当Redis重启时,会从头到尾执行一次AOF文件所包含的所有写命令,恢复AOF文件记录的Redis数据集。

AOF持久化方式Redis是默认关闭的,需要手动开启并且做一些配置

(2)AOF配置

配置文件:redis.conf。(同RDB服务端触发)

启动配置 / AOF文件名
在这里插入图片描述
刷盘策略(命令记录频率):推荐使用everysec
在这里插入图片描述

(3)优化(AOF重写)
  • AOF记录的是命令文本,RDB生成的内存快照dump.rdb是二进制文件,所以AOF文件体积会远大于RDB
  • 并且,一个key的所有相关命令会被AOF全部记录,但只有最后一条写命令有实际意义

客户端 手动重写优化:bgrewriteaof命令(bg rewrite aof)
在这里插入图片描述
服务端 自动触发重写:配置redis.conf。
在这里插入图片描述

4. 优缺点对比

在这里插入图片描述
注意:RDB和AOF不互斥。若系统既要求启动速度,又要求安全性,往往同时开启RDB和AOF,两者结合使用(混合持久化)。

5. 混合持久化

通过 AOF 后台重写(bgrewriteaof 命令)完成。

子进程先将当前全量数据以 RDB 方式写入新的 AOF 文件
然后再将 AOF 重写缓冲区的增量命令以 AOF 方式写入到文件
完成后,通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。

优点:系统数据获得更快的恢复速度更强的安全性
缺点:AOF 文件里面的 RDB 部分不再是 AOF 格式,可读性差

三. 数据过期策略

1. 定义(关键词:过期删除规则)

Redis对数据设置有效时间,数据过期以后,就需要将数据从内存中删除
我们可以按照不同的规则进行删除,具体的删除规则也称之为数据过期策略。

两种常用的数据过期策略:惰性删除、定期删除。
(回顾:在避免缓存击穿时,有一种策略叫做“逻辑过期”,即不删除过期数据,在首次命中过期缓存key时会获取互斥锁,开启新线程进行缓存重建,期间所有对key的请求皆返回过期数据。)

2. 惰性删除

在Redis中,为每个key都设置一个过期时间。

(1)不请求 key 时,则不去管该key是否过期:一直存在于内存;
(2)请求 key 时,则检测 key 是否过期:过期则删除,未过期则返回key的数据。

① 优势:CPU友好,不必频繁清理key。
② 劣势:内存不友好,内存中可能存在大量过期key。

3. 定期删除

每隔一段时间,我们就对一些key进行检测,过期则删除。

(1)SLOW模式定时任务模式。清理的默认频率10hz(每秒进行10次清理),每次清理耗时不超过25ms。可以通过redis.conf修改执行频率。
(2)FAST模式不定频模式。清理频率更快,但两次清理间隔不应低于2ms,每次清理耗时不超过1ms。

注意:无论是SLOW还是FAST模式,单次清理的耗时大概率是不足以覆盖所有缓存数据的(所以定义为“隔一段时间,检测一些key”)。单次清理时间耗尽必须停止,下次再接着检测。

① 优势:内存友好,定期删除过期key;可以通过限制清理的时常与频率降低对CPU的影响
② 劣势:难以确定删除操作的执行时常与频率。(过长则占用CPU,过短则无法及时释放内存。)

4. Redis的过期删除策略

“惰性删除 + 定期删除” 两种策略配合使用。

四. 数据淘汰策略

1. 定义(关键词:缓存多、内存满)

Redis中的内存不够用时,此时再向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删掉,这种数据的删除规则被称之为内存的淘汰策略。

2. Redis支持的8种数据淘汰策略

(1)noeviction【默认策略】
内存满时,不淘汰任何key,也不允许写入新数据,并直接报错。

(2)allkeys-random
所有key进行随机淘汰。

(3)volatile-ttl
剩余TTL越少,越先被淘汰。

(4)volatile-random
对于设置了TTL的key,进行随机淘汰。

(5)allkeys-lru【使用较多 / 推荐使用】
所有key采用LRU算法淘汰。

(6)volatile-lru
对于设置了TTL的key,采用LRU算法淘汰。

(7)allkeys-lfu
所有key采用LFU算法淘汰。

(8)volatile-lfu
对于设置了TTL的key,采用LFU算法淘汰。

补充1(单词解释):
① eviction 驱逐;no eviction不驱逐,不淘汰。
② volatile 易变的,代表着“有 生存时间 / 过期时间 的key”。
③ ttl = time to live生存时间。

补充2(LRU与LFU):
① LRU:Least Recently Used 最近最少使用,淘汰最长时间没有被使用的key
② LFU:Least Frequently Used 最少频率使用,淘汰一段时间内使用次数最少的key
③ LRU在全内存寻找“最长时间没被访问”的数据,一定程度上可以代表热点数据的保留;LFU仅寻找“某时间段内最少访问”的数据,时间段存在不确定性,所以LFU不能准确的代表热点数据的保留情况。

注意:应结合业务场景选择合适的数据淘汰策略。

3. 常见业务场景

  • 存在冷热数据:优先保留“热点数据”,选用allkeys-lru
  • 无明显冷热数据:随机淘汰,选用allkeys-random。
  • 存在置顶数据:置顶数据通常是热点key或重要数据,它们不设过期时间,淘汰策略选用volatile-lru。(这种策略组合也可以一定程度上预防缓存击穿问题)
  • 存在 “短时高频”访问数据:选用allkeys-lfu / volatile-lfu
  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值