查看原文
其他

面试被问到Redis的持久化策略别再不会啦

三友 三友的java日记 2022-10-31

欢迎点击关注公众号,学习更多进阶技术。

https://blog.csdn.net/SnailMann/article

/details/97787049


前提概念

  • 持久化的作用

  • Redis持久化的方式

RDB持久化策略

  • 什么是RDB?

  • RDB的两种策略方式

  • 如何使用RDB策略备份数据?

  • RDB的原理是什么?

AOF持久化策略

  • 什么是AOF

  • AOF的原理是什么

  • AOF的三种策略

  • AOF的重写机制

RDB和AOF的抉择

  • RDB还是AOF?

  • Redis4.0的混合持久化

  • 运维最佳实践

其他问题

  • RDB持久化操作时,子进程拷贝父进程的数据副本用于持久化,不会增加内存消耗吗?

  • 为什么Redis的AOF机制是先成功命令才再记录日志?

  • AOF文件过大会怎么办?


前提概念

持久化的作用

什么是持久化?

  • 我们将能把内存中的数据保存到磁盘中存储的行为就称之为持久化


为什么需要持久化?

  • Redis所有的数据保存在内存中,如何Redis实例突然的宕机,其占用的全部内存就会被系统释放,导致Redis的数据全部丢失,因此必须要有一种机制来保证Redis的数据不会因为故障而丢失。这一机制就是Redis的持久化机制。

  • 所以持久化操作的作用就是把Redis中的数据,保存到磁盘中。避免因为实例宕机,而出现数据丢失的现象。


Redis持久化的方式

Redis为我们提供了主要的两种持久化方式

  • 数据快照 (RDB)
    这种行为就类似MySQL的Dump ,redis的RDB,这就是某时某个点的全部数据备份

  • 日志文件(AOF)
    每次的操作就写入日志,当我们需要恢复数据时,就可以根据日志的记录完整的走一遍流程,恢复数据,比如MySQL的BinLog,Hbase的HLog,Redis的AOF


RDB持久化策略

什么是RDB?

什么是RDB数据快照?

  • RDB文件是一个二进制文件,存储在硬盘当中。其实就是之前用过的MySQL dump,我们把某个时刻的数据导出成RDB文件,需要的时候,导回去恢复数据即可

RDB的两种策略方式

  • save(同步)
    我在redis client执行save命令,它就会在Redis安装目录下生成一个RDB文件,在数据量比较大的时候,会造成一个同步阻塞。如果存在老的RDB文件,则替换。时间复杂度是O(n)

  • bgsave命令(异步)
    使用了linux的fork()函数生成了主进程的子进程,让这个子进程去完成RDB的生成。生成完毕后,会通知主进程,我们的RDB文件生成成功了。时间复杂度也是O(n),只不过不阻塞主进程

如何使用RDB策略备份数据?

支持自动,手动两种方式进行RDB持久化

redis可以通过客户端主动持久化,输入命令,生成RDB文件,也可以通过配置来等满足条件时自动持久化,生成RDB文件

通过客户端输入config get dir就可以知道Redis的数据存放路径。
通过客户端输入config get dbfireaname就可以知道rdb文件的名称



  • 手动持久化
    手动在客户端输入save 或 bgsave命令

  • 自动持久化
    通过配置,使得Redis在某个条件的时候自动触发RDB文件的生成,比如save m n配置
    save 900 1 900秒内至少有1个key被改变则做一次快照

    save 300 10 300秒内至少有300个key被改变则做一次快照

    save 60 10000 60秒内至少有10000个key被改变则做一次快照


什么情况下会主动触发Redis的RDB快照机制呢?

  • 主从复制时,从库全量复制同步主库数据,此时主库会执行bgsave命令进行快照;

  • 客户端执行数据库清空命令FLUSHALL时候,会触发快照的生成

  • 客户端执行shutdown关闭redis时,会触发快照的生成


RDB的原理是什么?

(一) RDB文件如何生成?

  • RDB快照文件即使就类似MySQL的DUMP文件,它就是一个快速数据,对某个时间节点的数据来个快照,然后备份到磁盘中。

  • 说白了当你要进行快照持久化时,Redis就会遍历内存中的所有数据,然后将数据序列化后写入到磁盘中的RDB文件


(二) 单线程模型的Redis进行RDB持久化,难道不会阻塞其他业务请求吗?


我们知道RDB持久化策略就是Redis在某个时间节点,扫描内存中的所有数据,生成一个二进制RDB文件,保存到本地。但是我们知道Redis是单线程模型的进程,既单个线程在处理客户端请求的同时,还要花点时间处理持久化生成RDB文件的耗时操作。那么问题就来了,生成RDB文件如此耗时,那么持久化的过程不就会阻塞我们正常的业务请求了吗?


答案是肯定会的,所以Redis为了解决这个问题,采用的是操作系统中的多进程COW机制 (Copy On Write) 来实现快照持久化


(三) 多进程COW机制 (Copy On Write)原理


什么是Copy On Write?


  • 相信Copy On Write这个名词,应该很多开发人员都知道,其作用就像Java中的CopyOnWriteArrayList的数据结构一样。当有人要写一个cow对象时,我们不会直接在该对象进行操作,而是复制一份它的副本数据,对该副本进行写操作,最后将修改后的副本替换原数据。它的作用就是可以实现对cow对象做到并发读-写不冲突,读-读不冲突。唯有写写才冲突


那什么又是多进程Copy On Write呢?


  • 我们都知道Redis是单线程模型,意思就是单个进程单个线程。但是实际上,Redis并非真的是单进程/线程的去处理事情。既主要的工作的确是单进程/线程模型,但也有一些很重要的,但比较耗时的操作,Redis并不是交给主进程/线程去做的,而是扔个子进程/线程去做异步操作!!

  • 既Redis在持久化时会调用glibc的函数,从主进程中fork出一个子进程,RDB快照持久化操作就是完全交给子进程异步去实现,而父(主)进程依然正常的继续处理客户端的业务请求。这里描述的就是多进程的体现

  • 而Copy On Write的体现就在于,RDB持久化操作,生成的是某一个时间节点的数据。比如我在12:00开始生成快照,那么不管之后Redis是否又对内存的数据做了什么操作,但我的RDB数据快照就一定是12:00这个时刻的数据。所以如果在12:00之后,还有其他客户端的写操作请求,那么Redis的父进程就会拷贝一份12:00的完整副本数据,专门用于做数据修改,而原数据则被子进程用于做RDB持久化的操作。所以主线程在12:00之后对数据的增删查改,是不会反应到快照数据中的。


AOF持久化策略

什么是AOF?

  • AOF就是一个日志文件,对Redis的每一条命令都会以AOF格式写入AOF文件中。需要恢复时,就导入AOF文件去执行里面的记录就可以了,而且记录肯定是实时的,毕竟每执行一条命令就会有相应的纪录

  • AOF日志存储的是Redis服务器的顺序指令序列,AOF日志只记录对内存进行修改的指令

  • AOF日志的记录并不像MySQL, HBase等数据库先写入日志再执行操作,而是先执行成功命令,再将命令记录到日志文件中。

  • 通过在配置文件配置appendonly参数为yes, 以开启AOF持久化


AOF的三种策略

(一) AOF写入原理

我们知道了Redis的AOF文件实际就是记录Redis实例的所有涉及修复内存的指令,但是我们要记录一个记录日志的特性


  • 但是这些修改指令的记录并不是直接刷到磁盘文件中的,而是①首先将内容写入内核的内存缓冲区,②然后再根据策略从内核异步刷新到硬盘的AOF文件中。

  • 这是为什么呢?因为这是一个效率问题,如果直接写入硬盘,速度较慢,有瓶颈。所以先写入缓冲区,保证吞吐量,然后再异步的刷新到硬盘中


(二) AOF写入的三种策略


所以Redis给我们提供了三种AOF策略,以供用户按需选择


  • always
    实时刷新,将缓冲区的每一条命令都立即刷新到硬盘的AOF文件。既每执行一条命令,待其刚刷入缓冲区,就立即强制刷入磁盘AOF文件中

  • everysec
    每一秒执行一次,既每一秒都强制将缓冲区的内容刷新到硬盘AOF文件中。

  • no
    不强制刷入,no就是不需要我们来考虑什么时候刷入磁盘,而是由操作系统来决定,他想什么时候刷新就什么时候刷新


(三) 三种策略的优缺点比较


  • always实时刷新策略的好处是可以保证,AOF文件是实时更新的,可以保证AOF日志肯定是完整的,不会出现AOF日志内容丢失的情况。但是缺点也很大,就是每次写操作,就要立马刷入日志,使得Redis的内存操作者和磁盘IO操作被同步捆绑,以至于产生大量的同步IO操作,造成Redis的写性能严重下降。影响了Redis高性能的口碑

  • everysec每秒一次策略,可以保证AOF文件每秒都能刷入一批指令记录。既在安全性和性能上进行平衡考虑的折中策略。好处就是在保证性能的同时,一定程度的保证数据的持久性,最多丢失1s的数据。缺点就是在高并发的场景下,1s中也可能会产生大量缓存数据,丢失1s的重要数据可能会造成生产事故

  • no这个机制比较少用,因为基本上都不能用在生成环境中。因为同步日志的时间间隔不能确定,完全靠操作系统自主决定。所以一旦宕机,可能会丢失一段时间的数据。


这三种策略的写回时机,以及优缺点汇总在了⼀张表格⾥,以⽅便你随时查看。


RDB和AOF的抉择

RDB还是AOF?

(1) RDB模式的优缺点


优点:

  • RDB快照非常适合做数据备份,因为RDB数据文件非常的紧凑,同样的数据大小,相比AOF日志会更小,节省磁盘空间。

  • RDB快照恢复起来,也比AOF日志要更快


缺点:

  • 生成RDB文件,通常是遍历整个内存数据块,所以即使是fork的子进程去做,也需要一定的资源消耗,时间可能会比较长。

  • 因为生成一个完整的RDB快照数据文件,需要一定的时间,比如几分钟。而且在RDB快照生成期间,此时如果有数据的改动,是不再会反应到RDB文件中的,所以一但数据库宕机,就可能丢失几分钟的数据。既数据安全性没有AOF日志高

  • RDB数据文件,可读性差,不能直接打开


(2) AOF模式的优缺点


优点:


  • AOF支持三种持久化策略,可以使用fysnc函数强制内核刷新缓存到AOF日志中,相比RDB模式,AOF可以更好的保护数据不丢失。

  • AOF日志文件可读性好,可以使用文本工具直接打开,执行了什么修改指令, AOF日志就记录什么。方便查看


缺点:


  • 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大。

  • 通过AOF日志文件进行数据恢复所需要的时间会更长,相比RDB模式,宕机的Redis实例恢复时间过长,就需要耽搁一段时间


(3) 选择?!


  • 如果你只是需要一个数据备份,不太在乎一小部分数据的丢失,那么你可以使用RDB模式。并且RDB文件也便于迁移,放到多个实例中进行数据恢复

  • 如果你相比之下,更在乎数据的完整性,既更看重数据的不丢失,那么你就可以使用AOF模式


Redis4.0的混合持久化模式

我们知道,如果采用RDB持久策略,一旦Redis宕机,就有可能丢失大量数据。但如果我们使用AOF持久策略,使用AOF日志重放的方式,一旦AOF日志大了,想比RDB的恢复耗时就显的非常慢。所以为了解决这样纠结的问题,Redis 4.0 带来了一个新的持久化特性 “混合式持久化”


什么是混合式持久化策略?


  • 混合式持久化策略,就是不再是单独的RDB快照模式或AOF日志文件模式,而是同时采用RDB快照和AOF日志两种持久化模式的混合持久化模式。具体表现就是会将RDB文件内容和增量的AOF文件内容都存放在同一个文件中(同一个aof格式文件)。

  • 因为RDB快照数据和AOF日志数据都存在同一个文件中,所以该aof格式文件就不再只存放全量的AOF日志了,而是前面的大部分存储的是RDB的快照数据,后一小部分存储的是自RDB快照持久化开始到持久化结束的这段时间发生的增量AOF日志。所以通常是RDB数据占了大头,AOF日志只是一小部分的增量日志

  • 在混合式的持久化策略下,当宕机的Redis实例重启时,会先加载RDB快照的内容,然后再重放增量的AOF日志。这样就可以替代之前的RDB全量导入或AOF全量重放啦!因此重启效率也大幅提升

  • 通过配置参数aof-use-rdb-preamble就可以开启混合式持久化方式,可以通过 config get aof-use-rdb-preamble命令来检查


混合持久化策略的优缺点


优点:结合了RDB和AOF的优点,使得数据恢复的效率大幅提升,有种“鱼和熊掌都可得兼”的感觉


缺点:兼容性不好,redis-4.x新增,虽然最终的文件也是.aof格式的文件,但在4.0之前版本都不识别该aof文件,同时也因为前部分是RDB格式,阅读性很差。


运维最佳实践

  • 快照是通过fork子进程来执行的,它是一个比较耗时的操作,大块写磁盘会加重磁盘负载。默认持久化不建议只使用RDB的方式。建议使用AOF模式或混合式持久化模式

  • AOF的方式有三种策略,不建议在生产环境中使用always, 因为fysnc是一个耗时的IO操作,它会降低Redis的性能。也不建议使用no策略,因为让操作系统来决定如何同步磁盘,这时间可长可短,不安全,所以建议使用everysec策略

  • 在集群环境中,不建议使用Redis的主节点都进行持久化操作,而是放到其从节点进行。因为从节点是备份节点,一般没有来自客户端的请求压力,所以它的操作系统资源一般都比较充沛

  • 做完缓存集群的网络和资源监控检查,同时要注意一点的是,要给Redis实例所在的机子预留一部分的内存,用于给fork的子进程使用


其他问题

RDB持久化操作时,子进程拷贝父进程的数据副本用于持久化,不会增加内存消耗吗?

我们知道了解到,Redis的RDB持久化是基于多进程COW机制实现的。我们也简单的讲解了Copy On Write是什么。但是Redis的多进程COW机制也并非就仅仅是复制数据副本来生成数据快照这么简单的,之前说的这么简单,只是为了更容易理解


那么,那么复杂的情况是怎么个样子呢?


  • RDB持久化操作期间,由子进程在做数据持久化,子进程并不会修改现有的内存数据结构,它只是对内存数据结构进行遍历读取,然后序列化写到磁盘中。然而父进程则不同,因为即使持久化期间,也可能会多个客户端还在不断的向Redis发送读写请求,所以父进程需要不断的对内存数据结构进程修改和更新操作。


因为子进程生成的数据快照肯定是某个时间节点的,而父进程可能又在时刻改变子进程要扫描的内存数据结构,那怎么办呢?


  • COW机制中的父子进程并非是完全独立的进程,他们之间的关系更像是一个连体婴儿,虽然是两个进程,但是实际在子进程刚刚被fork出来的时候,父子进程实际共享的是同一段内存空间,就相当于有两个头,但却共享一个身体,不过之后还是会慢慢剥离的,前期只是为了尽量共享,节约资源。

  • 此时Redis,就会使用操作系统的COW机制进程数据段页面的分离(数据段是有很多操作系统的页面组合而成)。当父进程对其中一个页面的数据进行修改的时候,会将被共享的数据页面复制一份分离出来,然后对这个复制的页面进行修改。此时子进程想应的页面是没有变化的,依然是子进程产生时那一刻的数据

  • 随着父进程接收的写请求越来越多,要复制出来修改的共享页面也越来越多。所以此时内存就会持续增长,但是由于被修改数据的比例一般占Redis总数据的比例不会太大,所以内存也不会增加原来两倍的情况。当然如果你在持久化期间,全部数据都被修改了个遍,那么两倍内存还是有希望的。


为什么Redis的AOF机制是先成功命令才再记录日志?

  • 如果Redis是先写日志后执行命令的话,可能会导致很多的错误命令都被记录到AOF文件中,这回加重AOF日志的负担


AOF文件过大会怎么办?

  • AOF文件会重写。简单来说,AOF重写机制就是在重写时,Redis根据数据库的现状创建⼀个新的AOF⽂件,也就是说,读取 数据库中的所有键值对,然后对每⼀个键值对⽤⼀条命令记录它的写⼊。重写机制具有“多变⼀”功能。所谓的“多变⼀”,也就 是说,旧⽇志⽂件中的多条命令,在重写后的新⽇志中变成了⼀条命令。


PS:如果觉得这篇文章对你有帮助,欢迎分享、点赞、在看;如果想持续获取更多技术干货,请关注公众号,感谢支持。


往期热门文章推荐

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存