Redis持久化机制

一、概述

Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。

Redis提供了一系列持久化选项:

  • RDB(Redis DataBase:内存快照):将某一时刻的内存数据,以二进制的方式写入磁盘;
  • AOF(Append Only File:增量日志):每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
  • 混合持久化方式(RDB + AOF):Redis 4.0新增的方式,集成了AOFRBD的优点;
  • 无持久化:完全禁用持久化。

默认使用rdb,redis 4.0以上默认使用混合持久化方式

二、RDB持久化

RDB(Redis DataBase),指的是在指定的时间间隔内将内存中的数据集快照写入磁盘,RDB是内存快照(内存数据的二进制序列化形式)的方式持久化,每次都是从Redis中生成一个快照进行数据的全量备份。

2.1 简介

RDB持久化方案进行备份时,Redis会单独fork一个子进程来进行持久化,会将数据写入一个临时文件中,持久化完成后替换旧的RDB文件。在整个持久化过程中,主进程(为客户端提供服务的进程)不参与IO操作,这样能确保Redis服务的高性能,RDB持久化机制适合对数据完整性要求不高但追求高效恢复的使用场景。

下面展示RDB持久化流程:

2.2 Fork

上面说到了RDB持久化过程中,主进程会fork一个子进程来负责RDB的备份,这里简单介绍一下fork

  • Linux操作系统中的程序,fork会产生一个和父进程完全相同的子进程。子进程与父进程所有的数据均一致,但是子进程是一个全新的进程,与原进程是父子进程关系;
  • 出于效率考虑,Linux操作系统中使用COW(Copy On Write:写时复制机制),fork子进程一般情况下与父进程共同使用一段物理内存,只有在进程空间中的内存发生修改时,内存空间才会复制一份出来。

Redis中,RDB持久化就是充分的利用了这项技术,Redis在持久化时调用glibc函数fork一个子进程,全权负责持久化工作,这样父进程仍然能继续给客户端提供服务。fork的子进程初始时与父进程(Redis的主进程)共享同一块内存;当持久化过程中,客户端的请求对内存中的数据进行修改,此时就会通过COW机制对数据段页面进行分离,也就是复制一块内存出来给主进程去修改。

RDB触发的规则分为两大类,分别是手动触发自动触发

自动触发:

  1. 配置触发规则
  2. shutdown触发
  3. flushall触发

手动触发:

  1. save命令
  2. bgsave命令

2.3 自动触发

以下介绍RedisRDB持久化机制中的自动触发机制中的配置触发规则来触发RDB,涉及到RDB规则的配置、文件存储路径配置、文件名配置、文件压缩配置、文件完整性校验配置。

2.3.1 配置规则触发

  • Redis安装目录下的redis.conf配置文件中,默认注释了下面三行数据,通过配置规则来触发RDB的持久化,需要开启或者根据自己的需求按照规则来配置。
# RDB核心规则配置
# 满足条件就将内存中的数据同步到硬盘中。若不想用RDB方案,可以把save ""的注释打开,下面三个注释
# save ""
# save <指定时间间隔> <执行指定次数更新操作>
save 3600 1   # 3600秒内有1个key被修改,触发RDB
save 300 100  # 300秒内有100个key被修改,触发RDB
save 60 10000 # 60秒内有10000个key被修改,触发RDB
  • 配置RDB文件的存储路径
# 数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
dir /usr/local/redis/var

我们可以在Redis的安装目录下看到dump.rdb文件,如果没看到,连接到客户端执行一次shutdown,这个是后面shutdown自动触发规则,后续会讲述。

  • 配置RDB文件的名称

# 指定本地数据库文件名,一般采用默认的 dump.rdb
dbfilename dump.rdb
  • 配置RDB文件压缩

Redis默认会使用LZF算法对RedisRDB文件进行压缩,这会消耗一定的CPU计算资源,但是会带来空间上的节省。

  • 配置RDB文件完整性校验

Redis默认使用CRC64的算法,对RDB文件完整性进行校验,以此来保证RDB文件的完整。

2.3.2 shutdown触发

shutdown触发RedisRDB持久化机制非常简单,我们在客户端执行shutdown即可。

2.3.3 flushall触发

flushall命令是清空redis内存中的数据,并且同时清空dump.rdb文件。所以这个命令就相当于删库跑路,此处只是说明该命令会触发rdb,实际使用中千万不要执行。

此时查看dump.rdb

执行flushall命令,后再次查看,rbd文件被清空。

2.4 手动触发

手动触发RDB持久化的方式可以使用save命令和bgsave命令,这两个命令的区别如下:

  • save:执行save指令,阻塞Redis的其他操作,会导致Redis无法响应客户端请求,不建议使用。
  • bgsave:执行bgsave指令,Redis后台异步进行快照的保存操作,此时Redis仍然能响应客户端的请求。

2.5 RDB持久化文件的备份

在实际的生产环境中,我们一般不会使用主节点Master来进行持久化备份,我们会通过在Redis的多个从服务器上进行RDB持久化备份,这样是为了对Redis数据的多次备份,防止出现网络分区或者部分节点宕机甚至是硬件损坏的情况发生。

应该要定时定期的通过脚本对Redis持久化文件进行转移备份,这样双重保险,更加可靠,万一遇到突发情况,也是多一手解决方案。

2.6 RDB优缺点

优点:

  • 存储紧凑,节省内存空间;
  • 恢复速度非常快;
  • 适合全量备份、全量复制的场景,经常用于灾难恢复(对数据的完整性和一致性要求相对较低的场合)。

缺点:

  • 容易丢失数据,容易丢失两次快照之间Redis服务器中变化的数据;
  • RDB通过fork子进程对内存快照进行全量备份,是一个重量级操作,频繁执行成本高;
  • fork子进程,虽然共享内存,但是如果备份时内存被修改,最大可能膨胀到2倍大小。

三、AOF持久化

AOF(Append Only File)是把所有对内存进行修改的指令(写操作)以独立日志文件的方式进行记录,重启时通过执行AOF文件中的Redis命令来恢复数据。AOF能够解决数据持久化实时性问题,是现在Redis持久化机制中主流的持久化方案(后续会谈到4.0以后的混合持久化)。

3.1 简介

Redis配置文件中开启,AOF持久化方案进行备份时,客户端所有请求的写命令都会被追加到AOF缓冲区(service.aof_buf)中,缓冲区中的数据会根据Redis配置文件中配置的同步策略来同步到磁盘上的AOF文件中,同时当AOF的文件达到重写策略配置的阈值时,Redis会对AOF日志文件进行重写,给AOF日志文件瘦身。Redis服务重启的时候,通过加载AOF日志文件来恢复数据。

3.2 AOF配置

AOF默认不开启,默认为appendonly no,开启则需要修改为appendonly yes

AOF配置文件的名称默认为appendonly.aof

配置文件的地址可以通过在redis客户端执行config get dir获取,其保存路径与RDB一致。

3.2.1 同步频率配置

AOF日志是以文件的形式存在的,当程序对AOF日志文件进行写操作时,实际上将内容写到了内核为文件描述符分配的一个内存缓冲区中,随后内核会异步的将缓冲区中的数据刷新到磁盘中。如果缓冲区中的数据没来得及刷回磁盘时,服务器宕机了,这些数据就会丢失。

因此Redis通过调用Linux操作系统的glibc提供的fsync(int fid)来将指定文件的内容强制从内核缓冲区刷回磁盘,以此来保证缓冲区中的数据不会丢失。不过这是一个IO操作,相比Redis的性能来说它是非常慢的,所以不能频繁的执行。

Redis配置文件中有三种刷新缓冲区的配置:

  • appendfsync always

每次Redis写操作,都写入AOF日志,这种配置理论上Linux操作系统扛不住,因为Redis的并发远远超过了Linux操作系统提供的最大刷新频率,就算Redis写操作比较少的情况,这种配置也是非常耗性能的,因为涉及到IO操作,所以这个配置基本上不会用

  • appendfsync everysec

每秒刷新一次缓冲区中的数据到AOF文件,这个Redis配置文件中默认的策略,兼容了性能和数据完整性的折中方案,这种配置,理论上丢失的数据在一秒钟左右

  • appendfsync no

Redis进程不会主动的去刷新缓冲区中的数据到AOF文件中,而是直接交给操作系统去判断,这种操作也是不推荐的,丢失数据的可能性非常大。

写回策略 写回时机 优点 缺点
always 同步写回 可靠性高、最大程度保证数据不丢失 每个写命令都要写回硬盘,性能开销大
everysec 每秒写回 性能适中 宕机时会丢失1秒内的数据
no 由操作系统控制写回 性能好 宕机时丢失的数据可能很多

3.2.2 刷新缓冲区

aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysecalwaysaof模式来说,执行fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no

如果对延迟要求很高的应用,这个字段可以设置为yes,否则还是设置为no,这样对持久化特性来说这是更安全的选择。

设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yesLinux的默认fsync策略是30秒,可能丢失30秒数据。

注意要刷新缓冲区的数据到磁盘需要将如下配置,配置为no,不是yes

no-appendfsync-on-rewrite no

3.3 AOF修复功能

AOF持久化机制正常恢复与RDB持久化机制的恢复是一样的,都只需要将备份文件放置到Redis的工作目录下,Redis启动时就会自动的加载。AOF持久化机制提供了AOF文件异常时恢复的功能,这个功能在AOF文件损坏的场景中经常被使用到。

测试

清空Redis服务中的数据。

写入数据。

AOF日志文件每秒会被刷新一次数据,此时数据已经写入了appendonly.aof文件。

打开文件我们可以非常清除的阅读AOF的文件内容,看到Redis的指令序列。

此时人为的进行数据破坏。

再次启动发现无法启动(我配置的别名启动)。

执行redis-check-aof --fix ../appendonly.aofAOF日志文件进行修复。

修复过程中会有部分数据丢失。

连接客户端查看数据。

3.4 AOF重写

前面提到AOF的缺点时,说过AOF属于日志追加的形式来存储Redis的写指令,这会导致大量冗余的指令存储,从而使得AOF日志文件非常庞大,比如同一个key被写了10000次,最后却被删除了,这种情况不仅占内存,也会导致恢复的时候非常缓慢,因此Redis提供重写机制来解决这个问题。RedisAOF持久化机制执行重写后,保存的只是恢复数据的最小指令集,我们如果想手动触发可以使用如下指令:

bgrewriteaof

Redis 4.0后的重写使用的是RDB快照和AOF指令拼接的方式,在AOF文件的头部是RDB快照的二进制形式的数据,尾部是快照产生后发生的写入操作的指令。

由于重写AOF文件时,会对Redis的性能带来一定的影响,因此也不能随便的进行自动重写,Redis提供两个配置用于自动进行AOF重写的指标,只有这两个指标同时满足的时候才会发生重写:

  • auto-aof-rewrite-percentage 100:指的是当文件的内存达到原先内存的两倍
  • auto-aof-rewrite-min-size 64mb:指的是文件重写的最小内存大小

AOF重写流程如下:

  1. bgrewriteaof触发重写,判断是否存在bgsave或者bgrewriteaof正在执行,存在则等待其执行结束再执行;
  2. 主进程fork子进程,防止主进程阻塞无法提供服务,类似RDB
  3. 子进程遍历Redis内存快照中数据写入临时AOF文件,同时会将新的写指令写入aof_bufaof_rewrite_buf两个重写缓冲区,前者是为了写会旧的AOF文件,后者是为了后续刷新到临时AOF文件中,防止快照内存遍历时新的写入操作丢失;
  4. 子进程结束临时AOF文件写入后,通知主进程;
  5. 主进程会将上面3中的aof_rewirte_buf缓冲区中的数据写入到子进程生成的临时AOF文件中;
  6. 主进程使用临时AOF文件替换旧AOF文件,完成整个重写过程。

3.5 AOF优缺点

优点:

  • 数据的备份更加完整,丢失数据的概率更低,适合对数据完整性要求高的场景;
  • 日志文件可读,AOF可操作性更强,可通过操作日志文件进行修复。

缺点:

  • AOF日志记录在长期运行中逐渐庞大,恢复起来非常耗时,需要定期对AOF日志进行重写(瘦身处理);
  • 恢复备份速度比较慢;
  • 同步写操作频繁会带来性能压力。

四、混合持久化

AOF与RDB对比

RDB AOF
持久化方式 定时对整个内存做快照 记录每一次执行的命令
数据完整性 不完整,两次备份之间会丢失 相对完整,取决于刷盘策略
文件大小 会有压缩,文件体积小 记录命令,文件体积大
宕机恢复速度 很快
数据恢复优先级 低,因为数据完整性不如AOF 高,因为数据完整性更高
系统资源占用 高,大量CPU和内存消耗 低,主要是磁盘IO资源
但AOF重写时会占用大量CPU和内存消耗
使用场景 可以容忍数分钟的数据丢失,追求更快的启用速度 对数据安全性要求较高

4.1 简介

RDB数据恢复虽然快,但是快照的频率不好把握。频率太低,会丢失比较多的数据,频率太高,会影响性能;AOF虽然能尽可能保证数据完整性,但是性能确实是一个诟病,比如重放恢复数据。

为了集合两者的优点,Redis 4.0提出了混合使用RDBAOF来做持久化,既保证了Redis重启速度,又降低数据丢失风险。

混合持久化工作在AOF日志重写过程,当开启了混合持久化时,在AOF重写日志时,fork出来的重写子进程会先将与主线程共享的内存数据以RDB方式写入到AOF文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以AOF方式写入到AOF文件,写入完成后通知主进程将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。

也就是说,使用了混合持久化,AOF文件的前半部分是RDB格式的全量数据,后半部分是AOF格式的增量数据。其日志文件结构如下:

混合持久化通过aof-use-rdb-preamble yes开启,Redis 4.0以上版本默认开启。

4.2 测试

我们先插入一些key,然后执行bgrewriteaof触发AOF持久化后,再插入一些key

此时将会看到如下的效果,验证了混合持久化的方式。

4.3 优缺点

混合持久化优点:

  • 混合持久化结合了RDBAOF持久化的优点,开头为RDB的格式,使得Redis可以更快的启动,同时结合AOF的优点,有减低了大量数据丢失的风险。

混合持久化缺点:

  • AOF文件中添加了RDB格式的内容,使得AOF文件的可读性变得很差;
  • 兼容性差,如果开启混合持久化,那么此混合持久化AOF文件,就不能用在Redis 4.0之前版本了。

五、总结

最后来总结这两者,到底用哪个更好呢?

  • 推荐是两者均开启;
  • 如果对数据不敏感,可以选单独用RDB
  • 不建议单独用AOF,因为可能会出现Bug
  • 如果只是做纯内存缓存,可以都不用。

六、拓展

6.1 如何选择合适的持久化方式?

  • 如果你的业务场景需要很高的性能,或者宕机之后能够尽快的恢复,而对数据完整性的要求不是那么高,那么可以采用RDB持久化的方式。
  • 如果你的业务场景对数据完整性的要求很高,那么可以采用AOF的持久化方式,而至于采用那种回写策略,则取决于你对数据完整性的要求程度。
  • 如果你的业务场景既要兼顾性能,又注重数据完整性,那么可以采用混合持久化的方式。
  • 如果你对数据丢失无所谓,追求性能最大化的情况下,你也可以不使用任何持久化方式。

6.2 Redis持久化数据和缓存怎么做扩容?

  • 如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容。
  • 如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。

参考文章

posted @ 2022-07-15 14:48  夏尔_717  阅读(813)  评论(0编辑  收藏  举报