redis持久化
redis提供了两种不同的持久化方式:RDB(redis database)和AOF(append only file)
RDB
RDB是redis默认采用支持持久化的方式,通过快照(snapshotting)实现持久化
触发条件
当满足一定条件时,RDB将对内存中的所有数据生成快照,并存放到硬盘中,默认存放在当前执行redis服务的根目录的dump.rdb中。
1.配置文件
RDB相关的配置通常在redis.conf中标识有SNAPSHOTTING注释的模块下,我们可以在该配置文件中设置触发快照生成的情况:
save <seconds> <changes>
save 3600 1 当3600秒内有1个key被修改
save 300 100 当300秒内有100个key被修改
save 60 10000 当60秒内有10000个key被修改
save "" 禁用快照,不将数据同步到快照文件中
其他相关的配置说明:
stop-writes-on-bgsave-error yes 当后台最后一次保存出错,停止redis的写操作。
rdbcompression yes 当进行持久化时,是否对数据使用LZF算法进行压缩。
rdbchecksum yes 在存储快照后,是否使用CRC64算法进行数据校验。
dbfilename dump.rdb 指定生成的快照文件名为dump.rdb。
dir ./ 存储快照文件的路径,./表示当前路径,可以在进入redis服务后通过config get dir查看。
2.执行save、bgsave、flushall、shutdown命令
save和bgsave命令这两个命令都是针对RDB持久化的,只要执行这两个命令中的其中一个都会将内存中的数据以RDB持久化的形式进行保存到磁盘上,不管你的redis服务中没有没开启RDB持久化。
save命令是让redis仅有的一个主进程来完成工作,此时redis处于堵塞状态不再响应其他客户端请求,需谨慎使用。
bgsavg命令执行后,会立刻返回OK,Redis会fork一个子进程,原来的redis主进程继续执行后续操作,新fork的子进程负责将数据保存到磁盘,然后退出。在fork子进程过程中redis主进程是堵塞的,不能响应客户端请求。redis自动快照就是使用bgsave完成的。
flushall用于清空数据库,当使用该命令清空数据时,redis也会对快照文件进行清空,就会触发bgsave
shutdown:redis在关闭之前为了防止数据丢失,会将所有数据全部保存下来,以便下次启动快速启动
3.从节点SYNC
redis主从复制中,从节点执行全量复制操作,主节点会执行bgsave命令,并将rdb文件发送给从节点
工作方式
-
redis主进程收到命令并判断是否在执行bgrewriteaof(AOF文件重写过程),如果此时正好在执行则bgsave直接返回,不fork子进程,如果没有执行,进入下一个阶段
-
主进程调用fork方法创建子进程,在创建过程中redis主进程堵塞,不能响应客户端请求。
-
子进程创建完成后,bgsave命令返回"Background saving started",此时redis可以正常响应客户端请求。
-
子进程根据主进程的内存副本创建临时快照文件,当快照完成以后对原快照文件进行替换。
-
子进程发送信号给redis主进程完成快照操作,主进程更新统计信息,子进程退出。
AOF
在AOF模式下,redis会将每一个收到的写命令(包括flushall命令)都通过write函数追加到文件appendonly.aof的末尾,当 Redis 重新启时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。
工作方式
redis将每一条写命令以redis通讯协议的方式添加至缓冲区aof_buf,在大量写请求的场景可以利用缓冲区暂存一部分命令,根据appendfsync配置的策略一次性写入磁盘,这样可以减少磁盘的I/O,提高性能。
触发条件
默认情况下redis并没有开启AOF,AOF相关的的配置一般都在在redis.conf中注释为APPEND ONLY MODE的模块里:
appendonly no 默认,如果要开启AOF,需要将no改为yes
appendfsync指定了redis进行aof持久化的时机:
appendfsync everysec 默认
-
always:每次收到写命令就立即fsync方法强制写入磁盘,性能最低,但是最能保证数据的完整性,不推荐使用
-
everysec:使用fsync每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐
-
no: 不使用fsync方法同步,而是交给操作系统write函数去执行同步操作,性能最好,不能保证数据的完整性。linux大约30s刷一次缓冲。这种情况,缓冲区数据同步不可控,在大量的写操作下,aof_buf缓存区堆积会越来越严重,一旦redis出现故障,数据会丢失
日志重写
随的时间推移aof文件越来越大,当aof文件大小超过所设定的阈值时,redis会启动aof文件的内容压缩,只保留可以恢复数据的最小指令集。
举个例子,如果你对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录(entry)。然而在实际上, 只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。
重写时,重复或者无效的命令不写入文件、过期的数据不再写入文件、多条命令合并写入(当多个命令能合并一条命令时候会对其优化合并作为一个命令写入 如:rpush list1 a rpush list b 合并为"rpush list1 a b")
重写触发
可以使用命令bgrewriteaof手动重写,也可以在redis.conf中配置的自动重写:
auto-aof-rewrite-min-size 64mb 最小重写值,当aof文件大于设置值才可能重写
auto-aof-rewrite-percentage 100 当前aof文件大小和上一次重写时的增长率
以上默认配置:当aof文件大小超过上次重写后大小的100%(2倍)且文件大于64mb时触发
重写过程
aof文件重写过程与rdb快照basave工作过程类似,都是通过fork子进程,由子进程完成响应的操作,同样的在fork子进程简短的时间内,redis是堵塞的。
-
开始bgrewriteaof,判断当前有没有bgsave命令/bgrewriteaof在执行,有等这些命令执行完在执行。
-
主进程fork出子进程,这个极短的过程中redis是堵塞的。
-
主进程fork完子进程继续接受客户端请求,所有写命令依然写入aof文件缓冲区并根据appendfsync策略同步到磁盘,保证原有aof文件完整和正确。由于fork的子进程仅仅只共享主进程fork时的内存,因此redis采用重写缓冲区机制保存fork之后的客户端的写请求,防止新的aof文件生成期间丢失这部分数据。此时,客户端的写请求不仅仅写入原来aof_buf缓冲,还写入重写缓冲区。
-
子进程通过内存快照,按照命令重写策略写入到新的aof文件
-
子进程写完新的aof文件后,向主进程发信号,父进程更新统计信息
-
主进程把aof_rewirte_buf中的数据写入到新的aof文件(避免写文件数据丢失)
-
-
使用新的aof文件覆盖旧的aof文件,标志aof重写完成
RDB与AOF比较
RDB优点:
rdb是一个非常紧凑的文件,体积小,传输速度快,适合灾难恢复。
rdb可以最大化redis性能,父进程在保存rdb文件时只需fork子进程,子进程就会进行保存操作,父进程无需执行任何磁盘I/O操作。
rdb在恢复大数据集速度比aof快。
RDB缺点:
rdb是快照过程,无法完成保存所有数据,特别在数据量大的时候,发生故障丢失的数据更多。
当redis数据量大,rdb需要对数据进行完成拷贝并生成快照文件,fork子进程会消化cpu,数据量越大,耗时越多。
rdb文件是特定的格式,阅读性差,因格式固定,可能会存在不兼容情况
AOF优点:
数据更完整,秒级数据丢失(取决于fsync策略设置)
兼容性好,aof文件是明文,基于reids通讯协议而形成的命令追加方式,阅读性好
AOF缺点:
数据文件体积较大,即使有重写机制,在相同的数据集,aof文件比rdb大
aof速度比rdb慢,数据量大的时候,恢复速度也比rdb慢
aof由于要频繁将命令同步到文件中,对性能的影响也比rdb大,但这种影响可接受