王道革 2020-06-14
RDB持久化方式是通过快照(snapshotting)完成的,当符合一定条件时,redis会自动将内存中所有数据以二进制方式生成一份副本并存储在硬盘上。当redis重启时,并且AOF持久化未开启时,redis会读取RDB持久化生成的二进制文件(默认名称dump.rdb,可通过设置dbfilename修改)进行数据恢复,对于持久化信息可以用过命令“info Persistence”查看。
该命令会由worker thread
执行,因此会阻塞 redis 的 worker
,期间不会响应任何其他客户端发来的请求,直到RDB快照文件执行完毕,所以慎用。
Copyinfo persistence # rdb_last_save_time:1570126868 save info persistence # rdb_last_save_time:1570126928
bgsave
“后台保存”。与save 最大的差异为它并不是由 worker thread
执行的,而是由redis fork
出子进程,交由子进程来完成持久化操作。
redis
在fork
子进程这个时间段内 redis是阻塞
的(此段时间不会响应客户端请求),当子进程创建完成以后redis才会响应客户端请求。
并且redis不会在控制台显示完成信息,只会写入日志。
客户端执行bgsave命令,redis主进程收到指令并判断此时是否在执行bgrewriteaof(AOF文件重新过程,后续会讲解),如果此时正好在执行则bgsave直接返回,不fork子进程,如果没有执行bgrewriteaof重写AOF文件,则进入下一个阶段;
主进程调用fork方法创建子进程,在创建子进程
过程中redis主进程阻塞,所以不能响应客户端请求;
子进程创建完成以后,bgsave
命令返回Background saving started
,此时标志着redis可以响应客户端请求了;
子经常根据主进程的内存副本创建临时快照文件,当快照文件完成以后对原快照文件进行替换;
子进程发送信号给redis主进程完成快照操作,主进程更新统计信息(info Persistence可查看),子进程退出;
Copybgsave Background saving started # 子进程创建成功,它会去 完成持久化操作
Copyconfig get logfile 11) "logfile" 12) "/var/log/redis/redis-server.log" cat "/var/log/redis/redis-server.log" 2497:M 04 Oct 2019 10:26:46.764 * Background saving started by pid 28960 # 开始后台 持久化 2497:M 04 Oct 2020 10:26:46.772 * Background saving terminated with success # 后台持久化完成
查看配置的方法:
1 查看配置文件
2 在redis 中查看当前 redis 的配置
CopyCONFIG get * # 获取所有的配置 CONFIG get dir # 获取 快照文件 保存的 位置 CONFIG get dbfilename # 获取 快照文件 的文件名
配置文件中 dir
指定
配置文件中 dbfilename
配置文件中 rdbcompression
default:yes
设置存储至本地数据库时是否压缩数据,默认为yes,采用LZF压缩
yes 会耗费一定的CPU资源,默认是 yes 。
no 会使存储的文件变大(巨大)
配置文件中 yesrdbchecksumy
default: yes
yes 会消耗一部分CPU资源,但是数据相对安全不容易损坏
no 可以节约读写性过程约10%时间消耗,但是存储一定的数据损坏风险
配置文件中 stop-writes-on-bgsave-error
default: yes
后台存储过程中如果出现错误线程,是否停止保存操作,默认是停止的
no 则忽略错误继续。
配置文件中 rdbchecksum
default: yes
在写入文件和读取文件时是否开启rdb文件检查,检查是否有无损坏,如果在启动是检查发现损坏,则停止启动。
配置文件中 save
default: yes
Copysave second changes # 查看默认的配置 config get save "900 1 300 10 60 10000" # 900秒内 1次键更新了就触发持久化 或 300秒内 10次更新 持久化 60 秒内 10000次更新 触发持久化 # 该持久化是 bgsave
手动执行 save
或 bgsave
以下几种种情况下会触发执行快照操作,并且默认的使用bgsave
主从复制时,从库全量复制同步主库数据,此时主库会执行bgsave
命令进行快照;
客户端执行数据库清空命令FLUSHALL
时候,触发快照;
客户端执行shutdown
关闭redis时,触发快照,也可以 使用 nosave
参数显式声明不保存快照
Copyshutdown nosave
当redis意外崩溃或者关闭再次启动时,此时AOF持久化未开启时(默认未开启),将使用RDB快照文件恢复数据。
Copy# 查看日志 cat /var/log/redis/redis-server.log 30448:M 04 Oct 2019 10:09:37.145 # Server initialized 30448:M 04 Oct 2019 10:09:37.145 * DB loaded from disk: 0.000 seconds # 从快照恢复 30448:M 04 Oct 2019 10:09:37.146 * Ready to accept connections
RDB方式无论是执行指令还是利用配置,无法做到实时持久化,具体较大的可能性丢失数据
bgsave指令每次运行要执行fork操作创建子进程,要牺牲掉一些性能
Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现个版本服务之间数据格式无法兼容现象
存储数据量较大,效率较低——基于快照思想,每次读写都是全部数据,当数据量巨大时,效率非常低
大数据量下的IO性能较低
日志形式的AOF,将命令追加到文件。AOF可以将Redis执行的每一条写命令追加到磁盘文件(appendonly.aof
)中,在redis启动时候优先选择从AOF文件恢复数据。因为要频繁的将每一个操作记录到文件中,所以开启AOF持久化会对性能有一定的影响,但是大部分情况下这个影响是可以接受的。
与RDB持久化相比,AOF持久化数据丢失更少,其消耗内存更少(RDB方式执行bgsve会有内存拷贝)。
redisAOF持久化过程可分为以下阶段:
redis将每一条写命令以redis通讯协议添加至缓冲区aof_buf,这样的好处在于在大量写请求情况下,采用缓冲区暂存一部分命令随后根据策略一次性写入磁盘,这样可以减少磁盘的I/O次数,提高性能。
当写命令写入aof_buf缓冲区后,redis会将缓冲区的命令写入到文件,redis提供了三种同步策略,由配置参数appendfsync
决定,下面是每个策略所对应的含义:
no
不使用fsync方法同步,而是交给操作系统write函数去执行同步操作,在linux操作系统中大约每30秒执行一次 sync
(man 2 sync 查看)。这种情况下,缓冲区数据同步不可控,并且在大量的写操作下,aof_buf缓冲区会堆积会越来越严重,一旦redis出现故障,数据丢失严重,整体不可控。
always
表示每次有写操作都调用fsync方法强制内核将数据写入到aof文件。这种情况下由于每次写命令都写到了文件中, 虽然数据比较安全,但是因为每次写操作都会同步到AOF文件中,所以在性能上会有影响,同时由于频繁的IO操作,硬盘的使用寿命会降低。
everysec
数据将使用调用操作系统write写入文件,并使用fsync每秒一次从内核刷新到磁盘。 这是折中的方案,兼顾性能和数据安全,所以redis默认推荐使用该配置。
当开启AOF时,随着时间推移,AOF文件会越来越大,redis提出一种重写的策略来缓解数据存储和恢复压力。
重复或无效的命令不写入文件
过期的数据不再写入文件
多条命令合并写入(当多个命令能合并一条命令时候会对其优化合并作为一个命令写入,例如“RPUSH list1 a RPUSH list1 b" 合并为“RPUSH list1 a b” )
AOF文件触发条件可分为手动触发和自动触发:
手动触发:客户端执行bgrewriteaof
命令。
自动触发:自动触发通过以下两个配置协作生效:
auto-aof-rewrite-min-size
AOF文件最小重写大小,只有当AOF文件大小大于该值时候才可能重写,4.0默认配置64mb。
auto-aof-rewrite-percentage
当前AOF文件大小和最后一次重写后的大小之间的比率等于或者等于指定的增长百分比,如100代表当前AOF文件是上次重写的两倍时候才重写。
redis在AOF功能开启的情况下,会维持以下三个变量
aof_current_size
:记录当前AOF文件大小
aof_rewrite_base_size
:记录最后一次AOF重写之后,AOF文件大小
aof_rewrite_perc
:增长百分比变量
每次当serverCron(服务器周期性操作函数)函数执行时,它会检查以下条件是否全部满足,如果全部满足的话,就触发自动的AOF重写操作:
没有BGSAVE命令(RDB持久化)/AOF持久化在执行;
没有BGREWRITEAOF在进行;
当前AOF文件大小要大于server.aof_rewrite_min_size的值;
当前AOF文件大小和最后一次重写后的大小之间的比率等于或者大于指定的增长百分比(auto-aof-rewrite-percentage参数)
AOF文件重写过程与RDB快照bgsave工作过程有点相似,都是通过fork子进程,由子进程完成相应的操作,同样的在fork子进程简短的时间内,redis是阻塞的,以下图文说明其重写过程:
开始bgrewriteaof
,判断当前有没有bgsave
命令(RDB持久化)/bgrewriteaof
在执行,倘若有,则这些命令执行完成以后在执行。
主进程fork出子进程,在这一个短暂的时间内,redis是阻塞的。
当完成重写后,将重写后的aof 文件合并到原有aof缓存文件中。并删除临时创建的重写后的aof文件
AOF实现本质是基于redis通讯协议,将命令以纯文本的方式写入到文件中。
redis协议:
首先Redis是以行来划分,每行以\r\n行结束。每一行都有一个消息头,消息头共分为5种分别如下:
(+)
表示一个正确的状态信息,具体信息是当前行+后面的字符。
(-)
表示一个错误信息,具体信息是当前行-后面的字符。
(*)
表示消息体总共有多少行,不包括当前行,*后面是具体的行数。
($)
表示下一行数据长度,不包括换行符长度\r\n,$后面则是对应的长度的数据。
(:)
表示返回一个数值,:后面是相应的数字节符。
我们可以直接查看AOF文件中的格式,如下图:
之前已经提到当AOF开启时候,redis数据恢复优先选用AOF进行数据恢复,以下使用停止redis来模拟redis故障,然后在重写启动进行恢复。
AOF文件最小重写大小,只有当AOF文件大小大于该值时候才可能重写,4.0默认配置64mb。
当前AOF文件大小和最后一次重写后的大小之间的比率等于或者等于指定的增长百分比,如100代表当前AOF文件是上次重写的两倍时候才重写。默认 100
上文中提到的三种策略,默认 everysec
always | everysec | no
当redis突然运行崩溃时,会出现aof文件被截断的情况,Redis可以在发生这种情况时退出并加载错误,以下选项控制此行为。
如果aof-load-truncated设置为yes,则加载截断的AOF文件,Redis服务器启动发出日志以通知用户该事件。
如果该选项设置为no,则服务将中止并显示错误并停止启动。当该选项设置为no时,用户需要在重启之前使用redis-check-aof
实用程序修复AOF文件在进行启动。
yes开启AOF,no关闭AOF 默认 no
指定AOF文件名,4.0无法通过config set 设置,只能通过修改配置文件设置。默认 appendonly.aof
redis4.0添加了新的持久化方式混合持久化
。
混合持久化默认是关闭的,它采用一种rdb
+ aof
的方式来实现,文件头rdb
一个全量的快照,格式为二进制,后面是 aof
格式。这样恢复会先查看开头是否为redis
,rdb开头必然是redis
。然后从快照恢复,之后从aof
恢复。
这样做的好处是可以结合 rdb 和 aof 的优点, 快速加载同时避免丢失过多的数据
缺点是 aof 里面的 rdb 部分就是压缩格式不再是 aof 格式,可读性差。
5 版本的redis 默认的开启了aof-use-rdb-preamble
但是 appendonly
默认是 no
也就是说,只要开启了aof 默认的会使用混合持久化。如果要使用单纯的aof
则需要手动的 将 aof-use-rdb-preamble
设为 no
(好坑)
4.0版本的混合持久化默认关闭的,通过aof-use-rdb-preamble
配置参数控制,yes则表示开启,no表示禁用,默认是禁用的,可通过config set修改。
创建子进程
生成全量的rdb快照,放在appendonly.aof 文件头部。然后,接下来将aof缓冲区的增量命令以aof方式写入文件尾
每次恢复数据时,首先从RDB部分恢复,然后执行aof。
开启了混合持久化时,启动redis依然优先加载aof文件,aof文件加载可能有两种情况如下:
aof文件开头是rdb的格式, 先加载 rdb内容再加载剩余的 aof。
aof文件开头不是rdb的格式,直接以aof格式加载整个文件。
RDB 是一个非常紧凑(compact)的文件,体积小,传输速度快,适合灾备。
RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork
出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。
RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快很多。
RDB 丢数据将会丢掉两次持久化之间的所有数据~
当redis中数据集比较大时候,RDB由于RDB方式需要对数据进行完成拷贝并生成快照文件,fork的子进程会耗CPU,并且数据越大,RDB快照生成会越耗时。
RDB文件是特定的格式,阅读性差,由于格式固定,并且多版本之间存在不兼容情况。很难受~
数据更完整,秒级数据丢失(取决于设置fsync策略)
兼容性较高,由于是基于redis通讯协议而形成的命令追加方式,无论何种版本的redis都兼容,再者aof文件是明文的,可阅读性较好。
数据文件体积较大,即使有重写机制,但是在相同的数据集情况下,AOF文件通常比RDB文件大。
相对RDB方式,AOF速度慢于RDB,并且在数据量大时候,恢复速度AOF速度也是慢于RDB。
由于频繁地将命令同步到文件中,AOF持久化对性能的影响相对RDB较大,但是对于我们来说是可以接受的。
混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
由于前部分是RDB格式,阅读性较差,兼容性差,一旦开启了混合持久化,4.0 版本之前的redis 都不认识这种aof文件~ 。