Redis-持久化
目录
AOF和RDB 追加单线程执行 某一瞬间内存数据--实际数据,太频繁会对性能产生影响,至少5分钟保存一次快照
AOF的三种磁盘写回策略? Redis 在执行写命令时对 AOF 文件的写入策略。 AOF是在硬盘中 AOF缓冲区是在内存
AOF的磁盘重写机制? 为了减小 AOF 文件体积而进行的一种优化操作 AOF重写缓冲区是在内存
RDB和AOF本质区别是什么? 文件类型:(二进制、文本) 安全性 文件回复速度 操作的开销
AOF重写你觉得有什么不足之处么? (写入两个地方)CPU、内存开销、)(刷盘)磁盘开销
针对AOF重写的不足,你有什么优化思路呢? 去掉重写缓冲区,将AOF日志分为两个日志,一个部分负责重写,另一个写入新增数据 最后组合成一个完整的AOF重写日志
持久化
AOF和RDB 追加单线程执行 某一瞬间内存数据--实际数据,太频繁会对性能产生影响,至少5分钟保存一次快照
AOF
- 每执行一条写操作命令,就将该命令以追加的方式写入到 AOF文件,然后在恢复时,以逐一执行命令的方式来进行数据恢复。
- 用AOF 日志的方式来恢复数据很慢,因为 Redis 执行命令由单线程负责的,AOF 日志恢复数据的方式是顺序执行日志里的每一条命令,如果 AOF 日志很大,这个过程就会很慢了。
RDB
- RDB快照是记录某一个瞬间的内存数据,记录的是实际数据,而AOF 文件记录的是命令操作的日志,而不是实际的数据。因此在 Redis恢复数据时,RDB恢复数据的效率会比 AOF 高些,因为直接将RDB 文件读入内存就可以,不需要像AOF 那样还需要额外执行操作命令的步骤才能恢复数据。
- RDB快照是全量快照,也就是说每次执行快照,都是把内存中的所有数据都记录到磁盘中。如果频率太频繁,可能会对 Redis性能产生影响。如果频率太低,服务器故障时,丢失的数据会更多。通常可能设置至少5分钟才保存一次快照,这时如果 Redis 出现宕机等情况,意味着最多可能丢失5分钟数据。
AOF-RDB混用
在AOF重写日志时,fork出来的重写子进程会先将与主线程共享的内存数据以RDB方式写入到AOF文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以AOF 方式写入到 AOF文件,写入完成后通知主进程将新的含有RDB 格式和 AOF 格式的AOF文件替换旧的的AOF文件。文件的前半部分是RDB 格式的全量数据,后半部分是AOF 格式的增量数据。
这样的好处在于,重启Redis 加载数据的时候,由于前半部分是 RDB内容,这样加载的时候速度会很快。加载完 RDB 的内容后,才会加载后半部分的AOF 内容,这里的内容是Redis 后台子进程重写 AOF期间,主线程处理的操作命令,可以使得数据更少的丢失。缺点是AOF文件的可读性变差了。
AOF重写期间AOF-RDB混用的具体流程:
- 主线程启动AOF重写:当触发AOF重写条件(例如达到auto-aof-rewrite-percentage和auto-aof-rewrite-min-size配置)时,主线程会启动AOF重写过程。
- Fork重写子进程:主线程通过fork系统调用创建一个子进程,该子进程将负责执行AOF重写操作。
- 子进程开始处理AOF重写:子进程首先会将与主线程共享的内存数据以RDB方式写入到新的临时AOF文件中。这个RDB格式的数据将成为新AOF文件的前半部分。
- 主线程继续处理命令:在子进程处理RDB数据的同时,主线程会继续处理新的命令请求,并将这些命令记录在重写缓冲区中。
- 子进程将增量命令写入AOF文件:在主线程处理命令期间,子进程会将主线程处理的增量命令以AOF格式写入到临时AOF文件的后半部分。这些增量命令将成为新AOF文件的后半部分。
- 完成AOF重写:子进程完成AOF重写后,会通知主进程。
- 主进程替换AOF文件:主进程收到子进程的通知后,会将新的临时AOF文件替换旧的AOF文件,确保持久化数据的一致性。
- 继续正常运行:重写完成后,Redis将继续正常运行,使用新的AOF文件进行持久化。
AOF的三种磁盘写回策略? Redis 在执行写命令时对 AOF 文件的写入策略。 AOF是在硬盘中 AOF缓冲区是在内存
Always、Everysec 和No,这三种策略在可靠性上是从高到低,而在性能上从低到高
Always是每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;
Everysec每次写操作命令执行完后,先将命令写入到AOF缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
No就是不控制写回硬盘的时机。每次写操作命令执行完后,先将命令写入到 AOF 缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。
AOF的磁盘重写机制? 为了减小 AOF 文件体积而进行的一种优化操作 AOF重写缓冲区是在内存
随着执行的命令越多,AOF 文件的体积自然也会越来越大,为了避免日志文件过大,Redis提供了AOF 重写机制,它会直接扫描数据中所有的键值对数据,然后为每一个键值对生成一条写操作命令,接着将该命令写入到新的AOF 文件,重写完成后,就替换掉现有的AOF 日志。重写的过程是由后台子进程完成的,这样可以使得主进程可以继续正常处理命令。
新的 AOF 文件只包含恢复数据所需的最小命令集,而不包含冗余或过期的命令。这不仅减少了磁盘空间的占用,还提高了加载速度,因为 Redis 仅需加载新 AOF 文件即可恢复数据。
AOF 重写的触发条件可以通过配置文件中的 auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size 选项进行设置。当 AOF 文件的大小超过给定百分比并且超过给定最小尺寸时,Redis 将触发自动的 AOF 重写。
为什么先执行Redis命令,再把数据写入AOF日志呢?
好处:
保证正确写入:如果当前的命令语法有问题,错误的命令记录到 AOF 日志里后可能还会进行语法检查。先执行Redis命令,再把数据写入AOF日志可以保证写入的都是正确可执行的命令。
不阻塞当前写操作:因为当写操作命令执行成功后才会将命令记录到AOF日志,避免写入阻塞。
缺陷:
数据可能会丢失:执行写操作命令和记录日志是两个过程,Redis还没来得及将日志写入到硬盘时发生宕机,数据会有丢失的风险。
阻塞其他操作:不会阻塞当前命令的执行,但因为 AOF 日志也是在主线程中执行,所以当 Redis 把日志文件写入磁盘的时候,还是会阻塞后续的操作无法执行。
AOF的重写的具体过程?
触发重写机制后,主进程会创建重写 AOF的子进程,此时父子进程共享物理内存,重写子进程只会对这个内存进行只读。重写AOF子进程读取数据库里的所有数据,并逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志。
在发生写操作的时候,操作系统才会去复制物理内存,这样是为了防止fork创建子进程时,由于物理内存数据的复制时间过长而导致父进程长时间阻塞的问题。
将AOF重写流程理解成"一次拷贝,两处缓冲”
- 一次拷贝:重写发生时,主进程会fork 出一个子进程,子进程和主进程共享Redis物理内存,让子进程将这些内存写入重写日志
- 两处缓冲:当重写时,有新的写入命令执行,会由主进程分别写入AOF缓冲区和 AOF 重写缓冲区;
-
- AOF缓冲区用于保证此时发生宕机,原来的AOF日志也是完整的,可用于恢复。
- AOF重写缓冲区用于保证新的AOF文件也不会丢失最新的写入操作。
AOF子进程的内存数据跟主进程的内存数据不一致怎么办?
Redis设置了一个AOF 重写缓冲区,这个缓冲区在创建 bg rewrite aof子进程之后开始使用。在重写 AOF期间,当Redis 执行完一个写命令之后,它会同时将这个写命令写入到AOF缓冲区和AOF 重写缓冲区。当子进程完成AOF 重写工作后,会向主进程发送一条信号。主进程收到该信号后,会调用一个信号处理函数,将AOF 重写缓冲区中的所有内容追加到新的 AOF 的文件中,使得新旧两个AOF 文件所保存的数据库状态一致;
新的AOF 的文件进行改名,覆盖现有的AOF文件。
Redis 的重写 AOF过程是由后台子进程 bgrewriteaof 来完成的,这有两个好处:
- 子进程进行AOF 重写期间,主进程可以继续处理命令请求,从而避免阻塞主进程;
- 子进程带有主进程的数据副本,使用子进程而不是线程,因为如果是使用线程,多线程之间会共享内存,那么在修改共享内存数据的时候,需要通过加锁来保证数据的安全,而这样就会降低性能。创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读的方式,而当父子进程任意一方修改了该共享内存,会发生写时复制,于是父子进程就有了独立的数据副本,不用加锁来保证数据安全。
RDB在执行快照的时候,数据能修改吗?
可以。执行 bgsave过程中,Redis 依然可以继续处理操作命令的,数据是能被修改的,采用的是写时复制技术 (Copy-On-Write, COW)。执行bgsave 命令的时候,会通过 fork ()创建子进程,此时子进程和父进程是共享同一片内存数据的,因为创建子进程的时候,会复制父进程的页表,但是页表指向的物理内存还是一个,由于共享父进程的所有数据,可以直接读取主进程里的内存数据,并将数据写入到RDB 文件。此时如果主线程执行读操作,则主线程和 bgsave 子进程互相不影响。如果主线程要修改共享数据里的某一块数据,就会发生写时复制,数据的物理内存就会被复制一份,主线程在这个数据副本进行修改操作。与此同时,子进程可以继续把原来的数据写入到RDB文件。
写时复制的基本原理是延迟数据的复制操作,直到需要修改数据时才进行复制。当某个进程或线程试图修改一个被共享的数据时,写时复制会先创建该数据的副本,然后将修改操作应用到副本上,而不是直接修改原始的共享数据。这样就避免了多个进程同时修改同一份数据时的数据竞争和冲突。
RDB和AOF本质区别是什么? 文件类型:(二进制、文本) 安全性 文件回复速度 操作的开销
本质区别就是RDB是使用快照进行持久化,AOF是日志。
- 文件类型:RDB生成的是二进制文件(快照 ),AOF生成的是文本文件(追加日志)
- 安全性:缓存宕机时,RDB容易丢失较多的数据,AOF根据策略决定(默认的everysec可以保证最多有一秒的丢失)
- 文件恢复速度:由于RDB是二进制文件,所以恢复速度也比 AOF更快
- 操作的开销:每一次RDB保存都是一次全量保存,操作比较重,通常设置至少5分钟保存一次数据。而AOF的刷盘是一次追加操作,操作比较轻,通常设置策略为每一秒进行一次刷盘
如果RDB和AOF只能选一种,你选哪个?
如果从业务需要来看,如果我们能接受分钟级别的数据丢失,可以考虑选择RDB,如果需要尽量保证数据安全,可以考虑混合持久化,如果只用AOF,那么优先选择everysec策略进行刷盘(在可靠和性能之间有一个平衡)。
从持久化理念来看,始终开启快照是一个推荐的方式,这也是Redis官方为什么默认开启RDB,而不开启AOF,同时官网也明确不推荐只开AOF
同时开启AOF和RDB,启动时加载哪个?
AOF和RDB同时开启,只会用AOF,即使此时AOF文件因为异常原因不存在,也不会用RDB,原因就是既然开启了AOF就是说明你想要AOF少丢数据的能力,所以即使没有AOF文件也不会用RDB,这样异常你也能发现及时处理,不然后者丢了数据就是潜在风险。
RDB本质是什么?
RDB本质是二进制形式的快照。
RDB持久化的触发时机?
从源码来看,RDB持久化函数整体上在这几个地方会被使用
- Redis Shutdown (Redis关闭之前进行一次持久化)
- 客户端发送save命令
- 客户端发送bgsave命令
- 每一次事件循环ServerCron检直是否需要bgsave (也就是判断我们的RDB配置)
- 主从全量复制发送RDB文件(也进行一次RDB持久化)
- 客户端执行数据库清空命令FLUSHALL
虽然RDB的触发条件有很多,但实际执行过程很简单,而区别点在于是子进程来执行还是主进程执行这个流程
- 打开一个临时的RDB文件(c语言库函数fopen)
- 将执行命令这一时刻数据库数据写入到 IO 缓冲区(c语言库函数fwrite)
- 会将这一时刻的数据按照RDB对应的版本格式进行写入
- 执行fflush(将IO缓冲区里的数据刷新到内核缓冲区)
- 执行fsync(可以将内核缓冲区里的数据刷到磁盘)
- 执行fclose (关闭这个临时文件)
- 修改临时文件名字,并让咱们的后台线程BIO_LAZY_FREE 去删除旧的RDB。
- 到此,一次RDB过程结束
简单来说RDB的流程就是触发RDB持久化时,让主进程或子进程(区分条件):(save或者bgsave)来将这一时刻的数据库数据写到一个新的RDB文件中
主要有这么几个地方,一个是调用save或者bgsave命令,一个是根据我们配置周期ServerCron进行,一个是Redis关闭之前,这三个是比较常见的,其它边缘一点的还有主从全量复制发送RDB文件等。(如果追问可以再说还有客户端执行数据库清空命令FLUSHALL)
save 和 bgsave
Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave,他们的区别就在于是否在「主线程」里执行:
- 执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程;
- 执行了 bgsave 命令,会创建一个子进程来生成 RDB 文件,这样可以避免主线程的阻塞;
RDB执行流程是怎样的?
首先,Fork出一个子进程来专门做RDB持久化
接着,子进程写数据到临时的RDB文件
最后,用新RDB文件替换旧的RDB文件
AOF刷盘的触发时机
整个AOF的流程可以分为三个过程:命令追加(append到aod_buf)、文件写入(write进内核缓冲区)、文件同步(fsync让内核缓冲区数据进入磁盘文件)
从源码来看,AOF刷盘函数在三个地方会被触发
- Redis Shutdown的时候
- 每一次事件循环 钩子函数 beforeSleep()
- 每一次事件循环的时间事件对应的handler--servercron()
- 通过配置指令关闭AOF功能时
而当我们设置的appendfsync策略不同,刷盘的情况也是不同的
- always(主进程)
Redis在每个事件循环都会将aof_buf缓冲区里的所有内容通过write 写入AOF文件描述符,并且调用fsync同步它(是在主线程中进行同步刷盘的),所以always 的效率也是最慢的(主线程直接跟磁盘IO打交道,单线程的redis 会被阻塞,有个慢速的落盘操作),但是从安全性角度来讲它也是最安全的。因为即使出现故障停机,AOF 持久化也只会丢失一个事件循环中所产生的命令数据(可以很多也可以很少, always 将变更都写入文件和同步,才能回复客户端响应成功)
- everysec(子进程)
服务器在每个事件循环都要将 aof_buf缓冲区的所有内容通过write 写入到AOF文件描述符,并且每隔超过一秒就要在子线程中对AOF 文件进行一次fsync同步(注意是在子线程中异步刷盘的,如果主线程发现子线程还在fsync,会推迟本秒的fsync)。
从效率上来讲,everysec足够快(后台线程进行fsync,主线程只进行write),并且就算出现故障停机,也只会丢失一秒钟的数据(实际上看源码是最多会丢失两秒多的数据)everysec是一种折中的取舍,也是默认值,也是推荐值
- no(交给操作系统)
no策略下redis 不会进行任何主动的fsync aof刷盘操作,所以它的效率对于redis来说也是最高的,不过因为把同步权交由给操作系统,所以no模式下的单次同步时长也是最长的,它会在系统缓冲区中积累一段时间的写入数据,然后才会真的被『落盘』。
从平摊操作的角度来讲,no跟everysec 的效率应该都是差不多的,但是从安全性来讲,如果出现故障停机,使用no会丢失上次同步AOF文件之后的所有命令数据。通常情况下如果使用no 策略,Linux会每隔30s刷盘,不过这取决于内核的具体配置
AOF触发流程主要有3个,一个是Redis关闭的时候,另一个是每一次事件循环钩子函数beforeSleep(),最后一个是每一次事件循环函数servercron里面
- beforeSleep() 是 Redis 事件循环中的一个钩子函数,在每一次事件循环结束之前被调用。
功能:这个钩子函数允许开发者在每个事件循环周期的末尾执行一些操作或逻辑。
示例用法:您可以在 beforeSleep() 中执行一些清理操作、更新状态、触发事件或其他自定义逻辑。
- serverCron() 是 Redis 事件循环中与时间事件相关的处理程序。
功能:Redis 使用时间事件来执行一些周期性的任务,例如定时持久化、过期键的删除等。
示例用法:serverCron() 是一个处理程序,它会根据预定的时间间隔执行一些操作。在每个事件循环周期中,Redis 会检查是否有时间事件需要触发,如果有,就会调用相应的处理程序,如 serverCron()。
serverCron 就是事件循环,每执行一轮之后会执行一次这个函数
RDB对主流程有什么影响?
- 当执行阻塞式持久化的时候,由主进程进行RDB快照保存,会阻塞主进程;
- 当执行后台持久化时,由fork出的子进程来进行RDB快照保存
-
- 如果数据量比较大的时候,会导致fork子进程这个操作比较耗时,从而阻塞主进程(复制内存空间是需要较长的时间和较高的内存消耗)
- 由于采用了写时复制技术,如果在进行RDB快照保存的时候,有大量的写入操作执行,会导致主进程多拷贝一份数据,消耗大量额外的内存
AOF对主流程有什么影响?
明显的影响:使用AOF持久化,如果我们选择always的策略,每当Redis命令执行之后,需要由主进程进行write + fsync的操作,如果数据过大的话,主进程就会花费较大的时间用于写AOF日志,对后续请求影响较大(阻塞直到写AOF完成,才能响应客户端执行成功)
潜在的影响:
如果我们选择 everysecond策略,虽然fsync是交给后台线程BIO_AOF_FSYNC来完成,但是主进程还需要进行write操作,如果后台线程上一轮fsync没有完成,那么主进程进write的时候仍然会阻塞(因为 write 作用在一个正在 fsync的fd(文件描述符)上,会阻塞)
AOF重写是由fork出的子进程进行的,类似于上面提到的风险,fork子进程这个操作有可能阻塞主进程
- 当appendfsync使用always,如果AOF写入日志压力过大会导致主进程处理其他请求很慢
- 当appendfsync使用everysec,如果后台线程上一轮的fsync没有完成,会导致我们本轮主线程执行write被阻塞(直到fsync完成)
- 当AOF重写发生时,如果数据量比较大,会导致fork子进程这个操作比较耗时,从而阻塞主进程
Redis 4.0 的混合持久化了解吗?
在 Redis 中,RDB 持久化是通过创建数据的快照来保存数据的,而 AOF 持久化则是通过记录每个写入命令来保存数据的。
两种方式各有优缺点。RDB 持久化的优点是恢复大数据集的速度比较快,但是可能会丢失最后一次快照以后的数据。AOF 持久化的优点是数据的完整性比较高,通常只会丢失一秒的数据,但是对于大数据集,AOF 文件可能会比较大,恢复的速度比较慢。
在 Redis 4.0 版本中,混合持久化模式会在 AOF 重写的时候同时生成一份 RDB 快照,然后将这份快照作为 AOF 文件的一部分,最后再附加新的写入命令。
这样,当需要恢复数据时,Redis 先加载 RDB 文件来恢复到快照时刻的状态,然后应用 RDB 之后记录的 AOF 命令来恢复之后的数据更改,既快又可靠。
RDB 和 AOF 如何选择?
如果需要尽可能减少数据丢失,AOF 是更好的选择。尤其是在频繁写入的环境下,设置 AOF 每秒同步可以最大限度减少数据丢失。
如果性能是首要考虑,RDB 可能更适合。RDB 的快照生成通常对性能影响较小,并且数据恢复速度快。
如果系统需要经常重启,并且希望系统重启后快速恢复,RDB 可能是更好的选择。虽然 AOF 也提供了良好的恢复能力,但重写 AOF 文件可能会比较慢。
在许多生产环境中,同时启用 RDB 和 AOF 被认为是最佳实践:
- 使用 RDB 进行快照备份。
- 使用 AOF 保证崩溃后的最大数据完整性。
简单描述AOF重写流程
AOF重写就三个关键点
- 子进程读取Redis中的数据以字符串命令的格式(也可以看作AOF文件格式)写入到新AOF文件中
- 如果有新数据,由主进程将数据写入到aof重写缓冲区(aof_rewrite_buf)
- 当子进程完成重写操作后,主进程将aof重写缓冲区中的数据传输给子进程,然后子进程追加到新aof文件中,保证新AOF文件的完整性
AOF重写你觉得有什么不足之处么? (写入两个地方)CPU、内存开销、)(刷盘)磁盘开销
重写期间新的写命令,会将数据写入到两处地方(AOF缓冲和AOF重写缓冲)中,这是额外的CPU和内存开销;
重写时会将AOF缓冲和AOF重写缓冲分别写入到旧日志和新日志中,这是额外的磁盘开销
我认为主要有3点不足之处:
- 额外的CPU开销:
在重写时,主进程需要将新的写入数据写入到 AOF重写缓冲(aof_rewrite_buf)
主进程需要向子进程发送AOF重写缓冲的数据
子进程还需要将这些数据写入到新的AOF日志中
- 额外的内存开销:
在重写时,AOF缓冲和 AOF重写缓冲中的数据都是一样的(浪费了一份)
- 额外的磁盘开销:
在重写时,AOF缓冲需要刷入旧的AOF日志,AOF重写缓冲也需要刷入到新的AOF日志,导致在重写时磁盘多占一份数据
针对AOF重写的不足,你有什么优化思路呢? 去掉重写缓冲区,将AOF日志分为两个日志,一个部分负责重写,另一个写入新增数据 最后组合成一个完整的AOF重写日志
在Redis 7.0版本,就使用MP-AOF 方案对AOF重写做了优化
核心其实就是去掉原来的重写缓冲,同时将AOF日志拆分为 Base AOF日志,Incr AOF日志,由manifest来管理。重写时,还是开一个子进程,对Base AOF日志进行重写,但是新命令会往新的Incr AOF日志写,Incr AOF日志+新的Base AOF日志就构成了完整的新的AOF日志。子进程重写结束时,主进程会负责更新 manifest 文件,将新生成的BASE AOF和 INCR AOF 信息加进清单,并将之前的 BASE AOF 和INCR AOF 标记为HISTORY。
这些HISTORY 文件默认会被 Redis异步删除(unlink),一旦manifest 文件更新完成,就代表着整个AOFRW流程结束
MP-AOF: Multi Part AOF = one BASE AOF + many INCR AOFs
有新的写入命令,会由主进程写入到aof_buf,再将缓冲数据刷入新的IncrAOF日志。
Base AOF日志记录重写之前的命令;Incr AOF 日志记录重写时新的写入命令(正常AOF刷盘的时候写的是Incr AOF)
mainfest 用于追踪管理AOF文件
AOF混合持久化方案是什么?
- AOF混合持久化会使用RDB持久化函数将内存数据写入到新的AOF文件中(数据格式也是RDB格式)
- 而重写期间新的写入命令追加到新的AOF文件仍然是AOF格式。
- 此时新的AOF文件就是由RDB格式和 AOF格式组成的日志文件
为什么需要混合持久化
混合持久化实际是AOF的优化,还是AOF记录的方式,但是重写时候是使用子进程将当前数据库内容保存为RDB格式,写到新AOF文件,作为前半部分内容,后面新增的内容就和普通AOF时一样写入后面。
这样做的好处就是既有RDB文件小、加载快的优势,也有AOF持久化数据损失少的优势。
启动加载AOF文件时,怎么判断他是否是混合持久化
混合持久化的AOF文件里开头有REDIS这个标记,加载时候通过这个标记来进行的判断
设置过期时间
如果是简单的字符串对象,可以使用如下语法:
SET key value EX seconds:设置多少秒之后过期
SET key value PX milliseconds:设置多少毫秒之后过期
TTL key:查看还有多久过期
Redis 报内存不足怎么处理?
Redis 内存不足有这么几种处理方式:
- 修改配置文件 redis.conf 的 maxmemory 参数,增加 Redis 可用内存
- 也可以通过命令 set maxmemory 动态设置内存上限
- 修改内存淘汰策略,及时释放内存空间
- 使用 Redis 集群模式,进行横向扩容。
Redis过期删除策略?
三种过期删除策略:
定时删除:在设置 key的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器执行key的删除操作。
- 优点:内存可以被尽快地释放。定时删除对内存是最友好的。
- 缺点:定时删除策略对CPU不友好,删除过期 key可能会占用相当一部分CPU时间,CPU 紧张的情况下将CPU 用于删除和当前任务无关的过期键上,会对服务器的响应时间和吞吐量造成影响。
惰性删除:不主动删除过期键,每次从数据库访问key时检测 key是否过期,如果过期则删除该key
- 优点:只会使用很少的系统资源,对CPU最友好。
- 缺点:如果一个key已经过期,而这个key又仍然保留在数据库中,那么只要这个过期 key 一直没有被访问,它所占用的内存就不会释放。惰性删除策略对内存不友好。
定期删除:每隔一段时间随机从数据库中取出一定数量的key进行检查,并删除其中的过期key.(只取设置了定时时间的key,有一个对应的db)
- 优点:限制删除操作执行的时长和频率来减少删除操作对 CPU的影响,同时也能删除一部分过期的数据减少了过期键对空间的无效占用。
- 缺点:内存清理方面没有定时删除效果好,同时没有惰性删除使用的系统资源少。难以确定删除操作执行的时长和频率。
Redis 选择惰性删除+定期删除这两种策略配合使用,以求在合理使用CPU时间和避免内存浪费之间取得平衡。
Redis在访问或者修改key之前,都会调用expireIfNeeded函数对其进行检查,检查key是否过期:
- 如果过期,则删除该key,然后返回 null 客户端;
- 如果没有过期,不做任何处理,然后返回正常的键值对给客户端;
- 每秒进行 10 次过期检查,从过期字典中随机抽取20个key;
- 检查这20个key是否过期,并删除已过期的key;
- 已过期key的数量占比随机抽取key的数量大于25%,则继续重复步骤直到比重小于25%。
Redis可以存多少数据(maxmemory)
使用maxmemory配置,默认是被注释掉的,也就是默认值是0,在32位操作系统中,maxmemory的默认值是3G,因为32位的机器最大只支持4GB的内存,而系统本身就需要一定的内存资源来支持运行,默认3G相对合理
64位机器不会限制内存的使用,自己进行配置maxmemory 1024(默认字节)
maxmemory 1024KB
maxmemory 1024MB
maxmemory 1204GB
Redis的内存淘汰策略?
Redis 虽然默认使用惰性删除和定期删除两种过期策略,但是,仍然存在这样一批过期的 key,定期删除一直都没有扫描到它,而且这些key也一直没有被使用,那么它们就会一直在内存中存在。另外,随着Redis不断的插入新数据,最终就会造成内存空间不足的问题。
对于这种问题的解决,就使用到了内存淘汰策略。Redis的内存淘汰策略用于在内存满了之后,决定哪些key要被删除。
- 不进行数据淘汰的策略 noeviction默认策略
它表示当运行内存超过最大设置内存时,不淘汰任何数据,这时如果有新的数据写入,则会触发OOM,只是单纯的查询或者删除操作的话还是可以正常工作。
- 进行数据淘汰的策略
在设置了过期时间的数据中进行淘汰:
- volatile-random:随机淘汰设置了过期时间的任意键值
- volatile-ttl:优先淘汰更早过期的键值
- volatile-lru:淘汰所有设置了过期时间的键值中,最久未使用的键值
- volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值
在所有数据范围内进行淘汰:
- allkeys-random:随机淘汰任意键值
- allkeys-lru:淘汰整个键值中最久未使用的键值
- allkeys-lfu:淘汰整个键值中最少使用的键值
内存回收是什么时候发起
大于maxmemory且开启了淘汰策略的情况下,每次进行读写的时候,都会去检查是否需要释放内存,如果需要则会触发。
介绍下Redis LRU回收算法
LRU,即最近最久未使用,记录每个key的最近访问时间,淘汰最久未没访问到的数据。但是Redis并不是标准的LRU算法,而是做了一些优化
Redis LRU算法是标准的吗?为什么不用标准的分析
标准LRU需要维护双链表,内存成本巨大,所以Redis采用近似LRU采样来做淘汰:
具体步骤是随机采样n个key,这个采样个数默认为5,然后根据时间戳淘汰掉最旧的那个key,如果淘汰后内存还是不足,就继续随机采样来淘汰。
在3.0之后, Redis还针对近似LRU算法做了淘汰池优化,也就是维护一个候选池,池中的数据根据访问时间进行排序。第一次随机选取的key都会放入池中,然后淘汰掉最久未访问的,比如第一次选了5个,淘汰了1个,剩下4个继续留在池子里。随后每次随机选取的key只有空闲时间大于池子里当前空闲时间最小的key时,才会放入池中。当池子装满了,如果有新的key需要放入,则将池中空闲时间最大的key淘汰掉。
什么是LFU算法,为什么Redis要引入LFU算法
LRU的一个弊端是只看最近访问时间,而忽略了频率
LFU,即将访问频率也加入到影响因素中,具体而言,LFU同时记录了上一次访问时间戳和访问计数,访问计数同时受上一次访问时间和访问频率的影响,因为每次访问都可能会增加计数。
Redis持久化时对过期键会如何处理的?
RDB
RDB分为生成阶段和加载阶段:
生成阶段会对key进行过期检查,过期的key不会保存到RDB文件中;
加载阶段看服务器是主服务器还是从服务器,如果是主服务器,在载入RDB 文件时,程序会对文件中保存的键进行检查,过期键不会被载入到数据库中;
如果从服务器,在载入RDB文件时,不论键是否过期都会被载入到数据库中。
由于主从服务器在进行数据同步时,从服务器的数据会被清空。过期键对载入RDB文件的从服务器也不会造成影响。
AOF
AOF文件写入阶段和AOF重写阶段。
写入阶段如果数据库某个过期键还没被删除,AOF文件会保留此过期键,当此过期键被删除后,Redis会向AOF文件追加一条DEL命令来显式地删除该键值。
重写阶段会对 Redis中的键值对进行检查,已过期的键不会被保存到重写后的AOF文件中。
Redis主从模式中,对过期键会如何处理?
从库不会进行过期扫描,即使从库中的key过期了,如果有客户端访问从库时,依然可以得到key对应的值。从库的过期键处理依靠主服务器控制,主库在key到期时,会在AOF 文件里增加一条del指令,同步到所有的从库,从库通过执行这条del指令来删除过期的key.
Redis 阻塞?怎么解决?
Redis 发生阻塞,可以从以下几个方面排查:
- API 或数据结构使用不合理
- 通常 Redis 执行命令速度非常快,但是不合理地使用命令,可能会导致执行速度很慢,导致阻塞,对于高并发的场景,应该尽量避免在大对象上执行算法复杂 度超过 O(n)的命令。
- 对慢查询的处理分为两步:
-
- 发现慢查询: slowlog get{n}命令可以获取最近 的 n 条慢查询命令;
- 发现慢查询后,可以从两个方向去优化慢查询: 1)修改为低算法复杂度的命令,如 hgetall 改为 hmget 等,禁用 keys、sort 等命 令 2)调整大对象:缩减大对象数据或把大对象拆分为多个小对象,防止一次命令操作过多的数据。
- CPU 饱和的问题
- 单线程的 Redis 处理命令时只能使用一个 CPU。而 CPU 饱和是指 Redis 单核 CPU 使用率跑到接近 100%。
- 针对这种情况,处理步骤一般如下:
-
- 判断当前 Redis 并发量是否已经达到极限,可以使用统计命令 redis-cli-h{ip}-p{port}--stat 获取当前 Redis 使用情况
- 如果 Redis 的请求几万+,那么大概就是 Redis 的 OPS 已经到了极限,应该做集群化水品扩展来分摊 OPS 压力
- 如果只有几百几千,那么就得排查命令和内存的使用
- 持久化相关的阻塞
- 对于开启了持久化功能的 Redis 节点,需要排查是否是持久化导致的阻塞。
-
- fork 阻塞 fork 操作发生在 RDB 和 AOF 重写时,Redis 主线程调用 fork 操作产生共享内存的子进程,由子进程完成持久化文件重写工作。如果 fork 操作本身耗时过长,必然会导致主线程的阻塞。
- AOF 刷盘阻塞 每秒刷新时 硬盘压力过高时会产生主线程阻塞 当我们开启 AOF 持久化功能时,文件刷盘的方式一般采用每秒一次,后台线程每秒对 AOF 文件做 fsync 操作。当硬盘压力过大时,fsync 操作需要等 待,直到写入完成。如果主线程发现距离上一次的 fsync 成功超过 2 秒,为了 数据安全性它会阻塞直到后台线程执行 fsync 操作完成。
- HugePage 写操作阻塞 对于开启 Transparent HugePages 的 操作系统,每次写命令引起的复制内存页单位由 4K 变为 2MB,放大了 512 倍,会拖慢写操作的执行时间,导致大量写操作慢查询。
原文地址:https://blog.csdn.net/m0_50846237/article/details/140506758
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!