Redis - 两种持久化
笔记
持久化能让数据「备份」,防止 Redis 突然宕机后,数据消失。
# 1. 持久化
Redis 提供了 2 个不同形式的持久化方式
- RDB(Redis DataBase)
- AOF(Append Of File)
# 2. 持久化之RDB
# 什么是RDB
在指定的时间间隔内
将内存中的数据集快照
写入磁盘,也就是俗话讲的 Snapshot 快照,它恢复时是将快照文件直接读到内存里。
# 备份是如何执行的
Redis 会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中
,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件
。整个过程中,主进程是不进行任何 IO 操作的,这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加的高效。
RDB 的缺点 是最后一次持久化后的数据可能丢失。
RDB的工作原理
- 触发时机:RDB持久化可以通过配置文件中的规则自动触发,也可以通过执行
SAVE
或BGSAVE
命令手动触发。自动触发的规则通常基于时间间隔和数据变化的阈值(例如,在900秒内至少有1个键被修改)。 - 持久化过程:当RDB持久化被触发时,Redis会执行以下步骤:
- 创建子进程:Redis通过
fork
创建一个子进程来进行持久化工作,这样主进程可以继续处理客户端请求,避免阻塞。 - 写入临时文件:子进程将当前内存中的数据写入到一个临时文件中。这一过程不会对主进程的性能产生影响,因为所有的磁盘IO操作都是由子进程负责的。
- 替换旧的快照文件:一旦子进程完成数据的写入,它会将临时文件替换掉上一次持久化生成的快照文件。
- 创建子进程:Redis通过
# Fork
Fork 的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是这是一个全新的进程,并作为原进程的子进程。
在 Linux 程序中,fork() 会产生一个和父进程完全相同的子进程,但子进程在此后多会 exec 系统调用,出于效率考虑,Linux 中引入了「写时复制技术」。
写时复制(Copy-On-Write, COW)
Linux系统在执行fork
时会采用写时复制技术。这意味着父进程和子进程会共享同一份物理内存数据,只有在数据需要被修改时,相应的数据才会被复制一份给对应的进程,从而节省内存和减少复制的开销。
# RDB持久化流程
流程图:
文件名
redis.conf 中默认的 RDB 配置文件名为 dump.rdb,可以修改文件名,但是一般默认就可以了
文件路径
rdb 文件的保存路径,也可以修改。默认为 Redis 启动时命令行所在的目录下
备份策略
RDB 是整个内存的压缩过的 Snapshot,RDB 的数据结构,可以配置复合的快照触发条件,默认:
- 如果 1 个 key 发生改变(新增,删除,修改),则 1 个小时后备份一次
- 如果 100 个 key 发生改变(新增,删除,修改),则 5 分钟后备份一次
- 如果 10000 个 key 发生改变(新增,删除,修改),则 1 分钟后备份一次
格式:
save <秒钟> <写操作次数>
RDB手动保存命令
我们可以使用两个命令手动备份,分别是 save
和 bgsave
。前提是需要连接上 Redis 服务
SAVE
命令:- 执行
SAVE
命令会导致Redis阻塞所有客户端请求,直到RDB文件被完全写入磁盘。 - 这个命令不建议在生产环境中使用,因为它会暂停所有对数据库的操作。
- 执行
BGSAVE
命令:- 执行
BGSAVE
命令时,Redis会在后台异步生成RDB文件,主进程不会被阻塞,Redis可以继续处理客户端请求。 - 这是生产环境中推荐的手动备份方式,因为它不会影响Redis的正常使用。
- 执行
- 查看最后一次备份时间:
- 通过
LASTSAVE
命令,可以查看Redis最后一次成功执行RDB持久化的时间戳。
- 通过
RDB持久化相关优化命令
FLUSHALL
命令:- 执行
FLUSHALL
命令会清除Redis中的所有数据,并生成一个空的RDB文件。这通常不是备份数据的目的,因此这个命令在数据备份方面没有实际的优化作用。
- 执行
stop-writes-on-bgsave-error
配置:- 当设置为
yes
时,如果后台保存(BGSAVE)失败,Redis将停止接受写操作,以避免数据不一致的问题。设置为no
时,即使BGSAVE失败,Redis也会继续接受写操作。 - 默认设置为
yes
,以确保数据一致性。
- 当设置为
rdbcompression
配置:- 这个配置项控制是否对RDB文件进行压缩。启用压缩(
yes
)会使用LZF算法减小RDB文件的大小,但同时会增加CPU的使用率。如果想要节省CPU资源,可以设置为no
关闭压缩功能。
- 这个配置项控制是否对RDB文件进行压缩。启用压缩(
rdbchecksum
配置:- 当启用(
yes
)时,Redis会在RDB文件保存完成后使用CRC64算法进行数据校验,以确保RDB文件的完整性和一致性。这会增加大约10%的性能消耗。如果在非常关注性能的场景下,可以设置为no
来关闭这个校验功能。
- 当启用(
# 禁用RDB持久化
在某些特定场景下,你可能希望禁用Redis的RDB持久化功能,以减少对磁盘的写操作或因为数据可以从其他地方恢复等原因。禁用RDB持久化可以通过以下两种方式实现:
# 通过配置文件禁用
- 编辑Redis的配置文件(通常命名为
redis.conf
),确保其中不包含任何save
指令。如果文件中已存在save
指令,你可以将它们全部注释掉(在行首添加#
),或者直接删除这些行。 - 另一种方法是在配置文件中添加一行
save ""
,这同样会禁用RDB持久化。
# 将所有的save指令注释掉,或者确保文件中不包含save指令
# save 900 1
# save 300 10
# save 60 10000
# 或者直接设置一个空的save指令
save ""
2
3
4
5
6
7
# 通过命令行禁用
- 连接到Redis服务器后,可以直接通过
config set save ""
命令来禁用RDB持久化。这个命令会立即生效,但是如果Redis服务器重启,除非这个设置被写入了配置文件,否则设置不会持久化。
redis-cli config set save ""
# RDB备份
即使你决定在运行时禁用RDB持久化,手动创建一个数据快照仍然是可能的,这在进行数据迁移或备份时特别有用。
- 首先,使用
config get dir
命令查询RDB文件当前存储的目录。 - 然后,可以使用标准的文件复制命令(如Linux的
cp
命令)将.rdb
文件拷贝到安全的备份位置。
手动触发RDB备份,并将备份文件拷贝到安全的位置。
- 查询RDB文件存储目录:
redis-cli config get dir
- 手动触发RDB备份(可选,如果你需要当前的快照):
redis-cli bgsave
- 拷贝RDB文件到备份位置:
假设RDB文件目录是/usr/local/bin
,备份目录是/opt/backups
:
cp /usr/local/bin/dump.rdb /opt/backups
# RDB恢复
当需要从RDB文件恢复数据时,可以按照以下步骤进行:
1. 关闭Redis服务:确保Redis服务器没有运行,以避免在数据恢复过程中发生数据写入。
根据你的操作系统和Redis安装方式,关闭Redis服务的命令可能会有所不同。例如,如果你使用的是systemctl
管理服务,可以使用:
systemctl stop redis
2. 拷贝备份文件:将之前备份的.rdb
文件拷贝回Redis的工作目录或安装目录下。确保这个文件的名称与配置文件中指定的名称一致(默认为dump.rdb
)。
cp /opt/backups/dump.rdb /usr/local/bin/dump.rdb
确保这个路径和redis.conf
中的dir
配置项一致,并且文件名通常应该是dump.rdb
。
3. 重新启动Redis:启动Redis服务后,它会自动从.rdb
文件中加载数据。
同样地,根据你的操作系统和服务管理工具,重新启动Redis的命令可能不同:
systemctl start redis
注意事项
- 确保在执行上述操作时,你有适当的文件和服务权限。
- 执行数据恢复之前,最好进行数据备份,以避免意外数据丢失。
- 在生产环境中操作前,建议先在测试环境验证以上步骤。
# RDB优缺点
优点:
- 适合大规模的数据恢复
- 对数据完整性和一致性要求不高更适合使用
- 节省磁盘空间
- 恢复速度快
缺点:
- Fork 的时候,内存中的数据被克隆了一份,大致 2 倍的膨胀性需要考虑
- 虽然 Redis 在 fork 时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能
- 在备份周期在一定间隔时间做一次备份,所以如果 Redis 意外 down 掉的话,就会丢失最后一次快照后的所有修改
# 3. 持久化之AOF
# 什么是AOF
以日志的形式来记录每个写操作(增量保存),将 Redis 执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis 启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
# AOF持久化流程
(1)客户端的请求写命令会被 append 追加到 AOF 缓冲区内;
(2)AOF 缓冲区根据 AOF 持久化策略 [always,everysec,no] 将操作 sync 同步到磁盘的 AOF 文件中;
(3)AOF 文件大小超过重写策略或手动重写时,会对 AOF 文件 Rewrite 重写,压缩 AOF 文件容量;
(4)Redis 服务重启时,会重新 load 加载 AOF 文件中的写操作达到数据恢复的目的;
# AOF开启
首先要知道 AOF 的默认配置文件名叫 appendonly.aof,如果想修改配置名,那么在 redis.conf 中配置文件里修改。
开启AOF持久化:使用文本编辑器打开redis.conf
配置文件,找到appendonly
配置项,将其值从no
修改为yes
:
修改AOF文件名(可选):如果需要修改AOF文件的名称,可以找到appendfilename
配置项,并设置为你希望的文件名:
appendfilename "appendonly.aof"
指定AOF文件保存路径:AOF文件默认与RDB快照文件保存在同一路径下,这个路径可以通过dir
配置项指定:
dir /path/to/redis/data
AOF 和 RDB 同时开启,redis 听谁的?
当Redis同时开启AOF(Append Only File)和RDB(Redis DataBase)两种持久化机制时,它们各自独立地按照各自配置的规则进行数据备份。然而,在数据恢复的场景下,Redis的行为会优先考虑AOF文件。
备份过程:AOF和RDB持久化机制可以同时进行数据备份,互不干扰。当它们各自的备份条件被满足时,Redis会分别执行AOF和RDB的备份操作。
恢复过程:当Redis启动时,如果同时存在AOF和RDB的备份文件,Redis会优先使用AOF文件来恢复数据。这是因为AOF通常包含比RDB更完整的数据恢复点。AOF以日志的形式记录了所有写操作,能够提供更精确的数据恢复能力,减少数据丢失的风险。
AOF 同步频率设置
AOF 同步频率设置只能三选一:
appendfsync always
:这个设置会导致Redis在每个写命令后都立即将数据同步到AOF文件。这提供了最高的数据安全性,但由于每次写操作都需要进行磁盘同步,会显著降低Redis的性能。appendfsync everysec
:这是一个中等偏好的设置,Redis会每秒同步一次数据到AOF文件。这样能在保证较好的数据安全性的同时,避免always
模式下的性能损失。在这种模式下,如果Redis突然宕机,最多可能丢失一秒钟的数据。appendfsync no
:在这个模式下,Redis不会主动同步数据到AOF文件,而是依赖操作系统的缓存策略来决定何时进行数据同步。这种方式提供了最高的性能,但如果系统崩溃,未同步的数据可能会丢失。
# AOF 启动/修复/恢复
AOF 的备份机制和性能虽然和 RDB 不同,但是备份和恢复的操作同 RDB 一样,都是拷贝备份文件,需要恢复时再拷贝到 Redis 工作目录下,启动系统即加载。
# AOF正常恢复示例
启用AOF持久化:首先,确保你的
redis.conf
配置文件中appendonly
选项被设置为yes
,这样Redis才会使用AOF持久化。appendonly yes
1找到Redis工作目录:在Redis客户端中,可以通过运行以下命令来查看Redis的工作目录。
redis-cli config get dir
1假设输出为
/var/lib/redis
,那么AOF文件应该显示在这个目录下。复制备份的AOF文件:将之前备份好的AOF文件复制到上一步找到的工作目录中。假设你的备份AOF文件位于
/backup/appendonly.aof
。cp /backup/appendonly.aof /var/lib/redis/appendonly.aof
1重启Redis服务:最后,重新启动Redis服务,这可以根据你的系统和Redis安装方式有所不同,例如使用
systemctl
:systemctl restart redis
1重启后,Redis会自动从指定的AOF文件中加载数据。
# AOF异常恢复示例
如果你怀疑AOF文件损坏或者Redis启动时报错提示AOF数据问题,可以使用redis-check-aof
工具进行修复。
备份损坏的AOF文件:在尝试修复之前,建议先备份原始的AOF文件。
cp /var/lib/redis/appendonly.aof /backup/appendonly.aof.bak
1修复AOF文件:使用
redis-check-aof
工具对AOF文件进行修复。确保你已经切换到包含AOF文件的目录,或者提供完整的AOF文件路径。redis-check-aof --fix /var/lib/redis/appendonly.aof
1如果发现问题,这个命令会尝试修复文件,并在修复过程中输出相关信息。
重启Redis服务:修复完成后,同样需要重新启动Redis服务以加载修复后的AOF文件。
systemctl restart redis
1
通过上述步骤,你可以在AOF文件正常或异常情况下进行数据的恢复或修复,确保Redis持久化数据的完整性和可用性。
# Rewrite重写
AOF 采用文件追加方式,文件会越来越大,为避免出现此种情况,新增了重写机制,当 AOF 文件的大小超过所设定的阈值时,Redis 就会启动 AOF 文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令 bgrewriteaof
。
重写原理,如何实现重写?
- 自动触发:Redis可以根据配置文件中的规则(如文件大小达到设定的阈值)自动触发AOF重写。
- 手动触发:通过执行
BGREWRITEAOF
命令,管理员也可以随时手动启动AOF重写过程。 - 执行过程:当AOF重写被触发时,Redis会
fork
一个新的子进程来进行重写操作。子进程首先将当前内存中的数据集以RDB格式写入临时文件的开头部分,然后追加之后执行的所有AOF命令。这种方式确保了即使在重写过程中,Redis也能持续提供服务并记录新的写操作。
no-appendfsync-on-rewrite 配置
这个配置项影响的是在进行AOF重写时,主进程是否应该执行fsync
操作(即将缓冲区内的数据强制写入硬盘)。
yes
:设置为yes
时,在AOF重写过程中,主进程不会进行fsync
操作。这样做可以避免重写时的性能损失,但如果在这个时候系统宕机,那么重写期间缓冲的数据可能会丢失。no
:设置为no
时,即使在进行AOF重写,主进程仍然会执行fsync
操作。这提高了数据安全性,但可能会因为fsync
操作导致短暂的性能下降。
假设希望自动触发AOF重写,同时在重写期间优先保证性能,你可以在Redis的配置文件redis.conf
中添加或修改以下配置项:
# 启用AOF持久化
appendonly yes
# 设置AOF重写自动触发的文件大小阈值
auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-percentage 100
# AOF重写期间,不执行fsync
no-appendfsync-on-rewrite yes
2
3
4
5
6
7
8
9
触发机制,何时重写?
Redis 会记录上次重写时的 AOF 大小,默认配置是当 AOF 文件大小是上次 rewrite 后大小的一倍且文件大于 64M 时触发。
重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定 Redis 要满足一定条件才会进行重写。
Redis决定何时启动AOF重写的条件包括两个配置参数:auto-aof-rewrite-percentage
和auto-aof-rewrite-min-size
。这两个参数共同工作,确保只在文件增长到一定比例且文件大小超过一定阈值时,才触发AOF重写。
auto-aof-rewrite-percentage
:这个参数设置了AOF文件相对于上一次重写后的大小增长了多少百分比时触发重写。例如,如果设置为100%,则表示当AOF文件大小达到上次重写后大小的两倍时触发重写。auto-aof-rewrite-min-size
:这个参数设置了触发重写的AOF文件的最小大小。这是为了避免在AOF文件较小的情况下频繁重写。例如,如果设置为64MB,则AOF文件大小至少达到64MB才考虑重写。
系统载入时或者上次重写完毕时,Redis 会记录此时 AOF 大小,设为 base_size,如果 Redis 的 AOF 当前大小 >= base_size + base_size * 100%
(默认)且当前大小 >= 64mb(默认)的情况下,Redis 会对 AOF 进行重写。
重写流程
bgrewriteaof 触发重写,判断是否当前有 bgsave 或 bgrewriteaof 在运行,如果有,则等待该命令结束后再继续执行
主进程 fork 出子进程执行重写操作,保证主进程不会阻塞
子进程遍历 redis 内存中数据到临时文件,客户端的写请求同时写入
aof_buf
缓冲区(旧文件继续存储数据)和aof_rewrite_buf
重写缓冲区(新文件的数据),保证原 AOF 文件完整以及新 AOF 文件生成期间的新的数据修改动作不会丢失子进程写完新的 AOF 文件后,向主进程发信号,父进程更新统计信息
主进程把
aof_rewrite_buf
中的数据写入到新的 AOF 文件。使用新的 AOF 文件覆盖旧的 AOF 文件,完成 AOF 重写
# AOF优缺点
优点:
备份机制更稳健,丢失数据概率更低
可读的日志文本,通过操作 AOF 稳健,可以处理误操作
缺点:
- 比起 RDB 占用更多的磁盘空间
- 恢复备份速度要慢
- 每次读写都同步的话,有一定的性能压力
- 存在个别 Bug,可能造成无法恢复
AOF 的日志文件内容基本都是敲的命令,所以日志非常容易阅读。
# 4. 两者总结
# 用哪个好
官方推荐两个都启用。
AOF 和 RDB 同时开启:
- 备份数据时,只要满足自己的备份条件,两个都会进行备份
- 恢复数据时,系统默认取 AOF 的数据(数据不会存在丢失)
如果对数据不敏感,可以选单独用 RDB。
不建议单独用 AOF,因为可能会出现 Bug。
如果只是做纯内存缓存,可以都不用。
# 总结
RDB 持久化方式能够在指定的时间间隔内对数据进行快照存储
AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF 命令以 Redis 协议追加保存每次写的操作到文件末尾
Redis 还能对 AOF 文件进行后台重写,使得 AOF 文件的体积不至于过大
只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化
同时开启两种持久化方式
在这种情况下,当 Redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整
RDB 的数据不实时,同时使用两者时服务器重启也只会找 AOF 文件,那要不要只使用 AOF 呢?建议不要,因为 RDB 更适合用于备份数据库(AOF 在不变化时不好备份),快速重启,而且不会有 AOF 可能潜在的 Bug,留着作为一个万一的手段
性能建议
因为 RDB 文件只用作后备用途,建议只在 Slave 上持久化 RDB 文件,而且只要 15 分钟备份一次就够了,只保留
save 900 1
这条规则如果使用 AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只 load 自己的 AOF 文件就可以了,代价:
- 一是带来了持续的 IO
- 二是 AOF Rewrite,将 Rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的
只要硬盘许可,应该尽量减少 AOF Rewrite 的频率,AOF 重写的基础大小默认值 64M 太小了,可以设到 5G 以上,默认超过原大小 100% 大小重 写可以改到适当的数值
如果不使用 AOF ,仅靠
Master-Slave Repllcation
实现高可用性也可以,能省掉一大笔 IO,也减少了 Rewrite 时带来的系统波动。代价是如果 Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的 RDB 文件,载入较新的那个,微博就是这种架构