Redis持久化机制
一、概述
Redis
是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis
中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis
重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。
Redis
提供了一系列持久化选项:
RDB
(Redis DataBase
:内存快照):将某一时刻的内存数据,以二进制的方式写入磁盘;AOF
(Append Only File
:增量日志):每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;- 混合持久化方式(RDB + AOF):
Redis 4.0
新增的方式,集成了AOF
和RBD
的优点; - 无持久化:完全禁用持久化。
默认使用
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
触发的规则分为两大类,分别是手动触发和自动触发:
自动触发:
- 配置触发规则
shutdown
触发flushall
触发
手动触发:
save
命令bgsave
命令
2.3 自动触发
以下介绍Redis
的RDB
持久化机制中的自动触发机制中的配置触发规则来触发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
算法对Redis
的RDB
文件进行压缩,这会消耗一定的CPU
计算资源,但是会带来空间上的节省。
- 配置
RDB
文件完整性校验
Redis
默认使用CRC64
的算法,对RDB
文件完整性进行校验,以此来保证RDB
文件的完整。
2.3.2 shutdown触发
shutdown
触发Redis
的RDB
持久化机制非常简单,我们在客户端执行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
,此时对于everysec
和always
的aof
模式来说,执行fsync
会造成阻塞过长时间,no-appendfsync-on-rewrite
字段设置为默认设置为no
。
如果对延迟要求很高的应用,这个字段可以设置为yes
,否则还是设置为no
,这样对持久化特性来说这是更安全的选择。
设置为yes
表示rewrite
期间对新写操作不fsync
,暂时存在内存中,等rewrite
完成后再写入,默认为no
,建议yes
。Linux
的默认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.aof
对AOF
日志文件进行修复。
修复过程中会有部分数据丢失。
连接客户端查看数据。
3.4 AOF重写
前面提到AOF
的缺点时,说过AOF
属于日志追加的形式来存储Redis
的写指令,这会导致大量冗余的指令存储,从而使得AOF
日志文件非常庞大,比如同一个key
被写了10000
次,最后却被删除了,这种情况不仅占内存,也会导致恢复的时候非常缓慢,因此Redis
提供重写机制来解决这个问题。Redis
的AOF
持久化机制执行重写后,保存的只是恢复数据的最小指令集,我们如果想手动触发可以使用如下指令:
bgrewriteaof
Redis 4.0
后的重写使用的是RDB
快照和AOF
指令拼接的方式,在AOF
文件的头部是RDB
快照的二进制形式的数据,尾部是快照产生后发生的写入操作的指令。
由于重写AOF
文件时,会对Redis
的性能带来一定的影响,因此也不能随便的进行自动重写,Redis
提供两个配置用于自动进行AOF
重写的指标,只有这两个指标同时满足的时候才会发生重写:
- auto-aof-rewrite-percentage 100:指的是当文件的内存达到原先内存的两倍
- auto-aof-rewrite-min-size 64mb:指的是文件重写的最小内存大小
AOF
重写流程如下:
bgrewriteaof
触发重写,判断是否存在bgsave
或者bgrewriteaof
正在执行,存在则等待其执行结束再执行;- 主进程
fork
子进程,防止主进程阻塞无法提供服务,类似RDB
; - 子进程遍历
Redis
内存快照中数据写入临时AOF
文件,同时会将新的写指令写入aof_buf
和aof_rewrite_buf
两个重写缓冲区,前者是为了写会旧的AOF
文件,后者是为了后续刷新到临时AOF
文件中,防止快照内存遍历时新的写入操作丢失; - 子进程结束临时
AOF
文件写入后,通知主进程; - 主进程会将上面
3
中的aof_rewirte_buf
缓冲区中的数据写入到子进程生成的临时AOF
文件中; - 主进程使用临时
AOF
文件替换旧AOF
文件,完成整个重写过程。
3.5 AOF优缺点
优点:
- 数据的备份更加完整,丢失数据的概率更低,适合对数据完整性要求高的场景;
- 日志文件可读,
AOF
可操作性更强,可通过操作日志文件进行修复。
缺点:
AOF
日志记录在长期运行中逐渐庞大,恢复起来非常耗时,需要定期对AOF
日志进行重写(瘦身处理);- 恢复备份速度比较慢;
- 同步写操作频繁会带来性能压力。
四、混合持久化
AOF与RDB对比
RDB | AOF | |
---|---|---|
持久化方式 | 定时对整个内存做快照 | 记录每一次执行的命令 |
数据完整性 | 不完整,两次备份之间会丢失 | 相对完整,取决于刷盘策略 |
文件大小 | 会有压缩,文件体积小 | 记录命令,文件体积大 |
宕机恢复速度 | 很快 | 慢 |
数据恢复优先级 | 低,因为数据完整性不如AOF | 高,因为数据完整性更高 |
系统资源占用 | 高,大量CPU和内存消耗 | 低,主要是磁盘IO资源 但AOF重写时会占用大量CPU和内存消耗 |
使用场景 | 可以容忍数分钟的数据丢失,追求更快的启用速度 | 对数据安全性要求较高 |
4.1 简介
RDB
数据恢复虽然快,但是快照的频率不好把握。频率太低,会丢失比较多的数据,频率太高,会影响性能;AOF
虽然能尽可能保证数据完整性,但是性能确实是一个诟病,比如重放恢复数据。
为了集合两者的优点,Redis 4.0
提出了混合使用RDB
和AOF
来做持久化,既保证了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 优缺点
混合持久化优点:
- 混合持久化结合了
RDB
和AOF
持久化的优点,开头为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
集群可以做到这样。