Redis数据持久化
Redis作为一个内存数据库,数据是以内存为载体存储的,那么一旦Redis服务器进程退出,服务器中的数据也会消失。为了解决这个问题,Redis提供了持久化机制,也就是把内存中的数据保存到磁盘当中,避免数据意外丢失
Redis提供了两种持久化方案:RDB持久化和AOF持久化,
- 一个是快照的方式(RDB),
- 一个是类似日志追加的方式(AOF)
RDB快照持久化(默认持久化机制)
RDB持久化是通过快照的方式,即在指定的时间间隔内将内存中的数据集快照写入磁盘。
在创建快照之后,用户可以备份该快照,可以将快照复制到其他服务器以创建相同数据的服务器副本,
或者在重启服务器后恢复数据。RDB是Redis默认的持久化方式
快照持久化
RDB持久化会生成RDB文件,该文件是一个压缩过的二进制文件,可以通过该文件还原快照时的数据库状态,
即生成该RDB文件时的服务器数据。
RDB文件默认为当前工作目录下的dump.rdb
,可以根据配置文件中的dbfilename
和dir
设置RDB的文件名和文件位置
# 设置 dump 的文件名
dbfilename dump.rdb
# 工作目录
# 例如上面的 dbfilename 只指定了文件名,
# 但是它会写入到这个目录下。这个配置项一定是个目录,而不能是文件名。
dir ./
触发快照的时机
-
执行
save
和bgsave
命令 -
配置文件设置
save <seconds> <changes>
规则,自动间隔性执行bgsave
命令 -
主从复制时,从库全量复制同步主库数据,主库会执行
bgsave
-
执行
flushall
命令清空服务器数据 -
执行
shutdown
命令关闭Redis时,会执行save
命令
save和bgsave命令
执行save
和bgsave
命令,可以手动触发快照,生成RDB文件,
两者的区别如下:
-
使用
save
命令会阻塞Redis服务器进程,服务器进程在RDB文件创建完成之前是不能处理任何的命令请求127.0.0.1:6379> save OK
-
而使用
bgsave
命令不同的是,basave
命令会fork
一个子进程,然后该子进程会负责创建RDB文件,而服务器进程会继续处理命令请求127.0.0.1:6379> bgsave Background saving started
fork()
是由操作系统提供的函数,作用是创建当前进程的一个副本作为子进程子进程 copy 有父进程的所有数据
fork
一个子进程,子进程会把数据集先写入临时文件,写入成功之后,再替换之前的RDB文件,
用二进制压缩存储,这样可以保证RDB文件始终存储的是完整的持久化内容
自动间隔触发
在配置文件中设置save <seconds> <changes>
规则,可以自动间隔性执行bgsave
命令
save 900 1
save 300 10
save 60 10000
save <seconds> <changes>
表示在seconds秒内,至少有changes次变化,就会自动触发gbsave
命令
save 900 1
当时间到900秒时,如果至少有1个key发生变化,就会自动触发bgsave
命令创建快照save 300 10
当时间到300秒时,如果至少有10个key发生变化,就会自动触发bgsave
命令创建快照save 60 10000
当时间到60秒时,如果至少有10000个key发生变化,就会自动触发bgsave
命令创建快照
AOF日志持久化
除了RDB持久化,Redis还提供了AOF(Append Only File)持久化功能,
AOF持久化会把被执行的写命令写到AOF文件的末尾,记录数据的变化。
默认情况下,Redis是没有开启AOF持久化的,开启后,每执行一条更改Redis数据的命令,都会把该命令追加到AOF文件中,
但是,这是会降低Redis的性能,但大部分情况下这个影响是能够接受的,另外使用较快的硬盘可以提高AOF的性能
可以通过配置redis.conf
文件开启AOF持久化,关于AOF的配置如下:
# appendonly参数开启AOF持久化
appendonly no
# AOF持久化的文件名,默认是appendonly.aof
appendfilename "appendonly.aof"
# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的
dir ./
# 同步策略
# appendfsync always
appendfsync everysec
# appendfsync no
# aof重写期间是否同步
no-appendfsync-on-rewrite no
# 重写触发配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 加载aof出错如何处理
aof-load-truncated yes
# 文件重写策略
aof-rewrite-incremental-fsync yes
AOF的实现
AOF需要记录Redis的每个写命令,步骤为:命令追加(append)、文件写入(write)和文件同步(sync)
命令追加(append)
开启AOF持久化功能后,服务器每执行一个写命令,都会把该命令以协议格式先追加到aof_buf
缓存区的末尾,
而不是直接写入文件,避免每次有命令都直接写入硬盘,减少硬盘IO次数
文件写入(write)和文件同步(sync)
对于何时把aof_buf
缓冲区的内容写入保存在AOF文件中,Redis提供了多种策略
appendfsync always
:将aof_buf
缓冲区的所有内容写入并同步到AOF文件,每个写命令同步写入磁盘appendfsync everysec
:将aof_buf
缓存区的内容写入AOF文件,每秒同步一次,该操作由一个线程专门负责appendfsync no
:将aof_buf
缓存区的内容写入AOF文件,什么时候同步由操作系统来决定
appendfsync
选项的默认配置为everysec
,即每秒执行一次同步
关于AOF的同步策略是涉及到操作系统的write
函数和fsync
函数的,在《Redis设计与实现》中是这样说明的
为了提高文件写入效率,在现代操作系统中,当用户调用
write
函数,将一些数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区的空间被填满或超过了指定时限后,才真正将缓冲区的数据写入到磁盘里。这样的操作虽然提高了效率,但也为数据写入带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失。
为此,系统提供了
fsync
、fdatasync
同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保写入数据的安全性。
从上面的介绍我们知道,我们写入的数据,操作系统并不一定会马上同步到磁盘,所以Redis才提供了appendfsync
的选项配置。
当该选项时为always
时,数据安全性是最高的,但是会对磁盘进行大量的写入,Redis处理命令的速度会受到磁盘性能的限制;
appendfsync everysec
选项则兼顾了数据安全和写入性能,以每秒一次的频率同步AOF文件,即便出现系统崩溃,最多只会丢失一秒内产生的数据;
如果是appendfsync no
选项,Redis不会对AOF文件执行同步操作,而是有操作系统决定何时同步,不会对Redis的性能带来影响,但假如系统崩溃,可能会丢失不定数量的数据
AOF重写(rewrite)
在了解AOF重写之前,我们先来看看AOF文件中存储的内容是啥,先执行两个写操作:
127.0.0.1:6379> set s1 hello
OK
127.0.0.1:6379> set s2 world
OK
然后我们打开appendonly.aof
文件,可以看到如下内容
*3
$3
set
$2
s1
$5
hello
*3
$3
set
$2
s2
$5
world
该命令格式为Redis的序列化协议(RESP)。
*3
代表这个命令有三个参数,$3
表示该参数长度为3
看了上面的AOP文件的内容,我们应该能想象,随着时间的推移,Redis执行的写命令会越来越多,AOF文件也会越来越大,
过大的AOF文件可能会对Redis服务器造成影响,如果使用AOF文件来进行数据还原所需时间也会越长
时间长了,AOF文件中通常会有一些冗余命令,
比如:
-
过期数据的命令、
-
无效的命令(重复设置、删除)、
-
多个命令可合并为一个命令(批处理命令)。
所以AOF文件是有精简压缩的空间的
AOF重写的目的就是减小AOF文件的体积,
不过值得注意的是:AOF文件重写并不需要对现有的AOF文件进行任何读取、分享和写入操作,而是通过读取服务器当前的数据库状态来实现的
手动触发和自动触发
- 手动触发执行
bgrewriteaof
命令,该命令的执行跟bgsave
触发快照时类似的,都是先fork
一个子进程做具体的工作
127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
- 自动触发会根据
auto-aof-rewrite-percentage
和auto-aof-rewrite-min-size 64mb
配置来自动执行bgrewriteaof
命令
# 表示当AOF文件的体积大于64MB,且AOF文件的体积比上一次重写后的体积大了一倍(100%)时,会执行`bgrewriteaof`命令
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
触发重写的条件
-
AOF重写可以由用户通过调用
BGREWRITEAOF
手动触发。 -
服务器在AOF功能开启的情况下,会维持以下三个变量:
- 记录当前AOF文件大小的变量aof_current_size。
- 记录最后一次AOF重写之后,AOF文件大小的变量aof_rewrite_base_size。
- 增长百分比变量aof_rewrite_perc。
-
每次当serverCron(服务器周期性操作函数)函数执行时,它会检查以下条件是否全部满足,如果全部满足的话,就触发自动的AOF重写操作:
- 没有BGSAVE命令(RDB持久化)/AOF持久化在执行;
- 没有BGREWRITEAOF在进行;
- 当前AOF文件大小要大于server.aof_rewrite_min_size(默认为1MB),或者在redis.conf配置了auto-aof-rewrite-min-size大小;
-
当前AOF文件大小和最后一次重写后的大小之间的比率等于或者等于指定的增长百分比
(在配置文件设置了auto-aof-rewrite-percentage参数,不设置默认为100%)
重写的流程
1)触发重写,执行bgrewriteaof命令
2)父进程fork子进程进行重写,fork子进程的同时父进程阻塞,fork完毕父进程继续接受指令(子进程只是父进程的快照(相当于复制了某时刻的父进程))
3)子进程在创建新的aof文件的同时,父进程继续接收write指令,存储到继续存到aof_buf缓存中和aof_rewirte_buf缓存中,所以父进程继续往旧的aof文件中备份,同时也要往新的AOf文件中备份。
4)新的aof备份完成
5)同时父进程,备份的新文件创建完成
6)将aof_rewrite_buf缓存中的备份到新的aof文件中
- 新的文件替换就的aof文件
完成AOF重写之后
-
当子进程完成对AOF文件重写之后,它会向父进程发送一个完成信号,父进程接到该完成信号之后,会调用一个信号处理函数,该函数完成以下工作:
- 将AOF重写缓存中的内容全部写入到新的AOF文件中;这个时候新的AOF文件所保存的数据库状态和服务器当前的数据库状态一致;
- 对新的AOF文件进行改名,原子的覆盖原有的AOF文件;完成新旧两个AOF文件的替换。
-
当这个信号处理函数执行完毕之后,主进程就可以继续像往常一样接收命令请求了。
在整个AOF后台重写过程中,只有最后的“主进程写入命令到AOF缓存”和“对新的AOF文件进行改名,覆盖原有的AOF文件。
”这两个步骤(信号处理函数执行期间)会造成主进程阻塞,在其他时候,AOF后台重写都不会对主进程造成阻塞,这将AOF重写对性能造成的影响降到最低。
附录
https://blog.csdn.net/hezhiqiang1314/article/details/69396887
https://juejin.cn/post/6844903939339452430
https://mp.weixin.qq.com/s/PRPnt3xuAArsTNMxQshhTQ