Redis的存储是基于内存的,这就有意味着,如果服务重启那么所有的数据都会丢失,这是我们不能接受的,为此,Redis提供了 RDB 与 AOF 两种持久化机制
简介
RDB: 对于RDB,我们可以把它理解一个定时的快照,就是每隔一段时间(或其他策略),它会创建一个当时所有数据的一个快照,默认存储到的文件为 dump.rdb
AOF: 因为RDB创建的是所有数据的快照,这点就决定了它不可能进行高频率的执行,但是如果不经常性的执行,那么当出现如服务器宕机等情况时,会丢失从上次快照到当前的所有操作数据。为了解决这个问题,AOF应运而生,它会在每次执行操作的时候,将这个操作命令进行持久化存储,默认存储到的文件为 appendonly.aof,这样恢复的时候,可以将 aof文件中的所有命令重新执行一遍,即可恢复数据
配置及使用
RDB和AOF的配置都在redis的配置文件redis.conf中进行配置,我们看下主要的配置
RDB的主要配置如下
1 | rdbcompression yes # 是否使用 LZF算法 压缩字符串 |
同时,我们也可以通过执行save
(同步执行,会阻塞请求) 和bgsave
(另起子进程执行,可以同时接受其他命令执行) 手动触发数据快照的创建
AOF的主要配置为
1 | appendonly yes # 是否开启aof,默认关闭 |
对于AOF,有个重写的概念,因为它保存的是所有指定的修改数据指令,其中有许多是可以精简的,举个例子,比如有如下命令
1 | set k1 v1 |
我们看一眼就知道最终redis中是没有k1这个数据的,但是aof却要存储这两个指令,其实完全可以将这两个命令删除掉,当然还有很多其他情况下,也是可以对命令进行精简的
我们可以通过配置auto-aof-rewrite-percentage
和auto-aof-rewrite-min-size
来控制重新的触发条件,或者可以手动执行bgrewriteaof
来触发AOF重写
重写时,会根据现有的数据,重新生成新的AOF文件,之后将在重写执行过程中用户新产生的命令追加到此AOF文件中,最后替换老的文件
其中还有一个同步文件配置,是因为调用操作系统写入时,并不一定会实时写入硬盘,而是写入到一个内存缓冲区,之后由操作系统不定时写入到硬盘。这本来是操作系统提升写入速度的机制,但是对于Redis来说却有可能导致数据的丢失,所以可以通过配置appendfsync
控制刷到硬盘的频率,但这也需要平衡,每次都刷到硬盘会导致性能的下降,一般默认每秒一次即可
RDB和AOF配置及状态也可以通过执行info persistence
查看
重启恢复
在Redis重新启动时,因为aof数据相比rdb会更新一些,所以如果开启了AOF,并且aof文件存在,那么使用aof文件进行数据恢复,服务端日志打印
* DB loaded from append only file: 0.000 seconds
如果AOF关闭,rdb文件存在则加载rdb文件恢复数据,服务端打印如下日志
* DB loaded from disk: 0.000 seconds
文件结构
对于持久化的文件结构,我们可以简单看下
RDB文件
先来看下RDB的 dump.rdb 文件,RDB的文件格式有许多版本,最新的 redis5.0 使用的是 版本9,具体版本可以参考RDB_Version_History,而关于 RDB文件结构的文档,目前只找到了关于 版本7 的,最新的版本并没有找到对应的文档资料
我们就找个简单的例子来看一下,具体的大家可以参考上面的资料
我本地安装的redis版本是5.0的,就先看下对应的RDB文件吧,先flushall
清空数据后,执行set kk vv
,再手动执行一下bgsave
触发RDB
之后使用16进制的模式打开rdb文件,内容如下(右侧为对应的字符内容)
1 | 5245 4449 5330 3030 39fa 0972 6564 6973 | REDIS0009..redis |
这里我们就结合源码来看一下,对应源码部分为(为了简单,已删除大部分内容)
1 | int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) { |
各个操作码对应的值为:
1 | /* Special RDB opcodes (saved/loaded with rdbSaveType/rdbLoadType). */ |
这里我们就可以和前面的文件内容进行对照了,最开始部分为 REDIS0009 为魔术 REDIS及版本9,后面的为aux域,这里我们先跳过,直接到我们关心的数据相关部分,内容为
所以现在直接跳过这部分内容,到数据部分,内容为
1 | 16进制: fe 00fb 0100 0002 6b6b 0276 76ff 3545 3136 adb7 3b22 |
依次顺序看下
FE 00
FE对应的10进制是254,即RDB_OPCODE_SELECTDB, 结合后面的00就表示 select 0
FB 01 00
这个对应的是 RDB_OPCODE_RESIZEDB 表示 DB中的key数量为1个,有过期时间的key为0个
0002 6B6B 0276 76
中,最开始的 00 通过0 = “String Encoding”
可以得知表示值类型为string,后面的 02 表示的是长度为2,之后的两个字节 6B6B(0x6B的10进制为107,对应ASCII码字符为k) 表示的是我们之前设置的键: kk,再之后的 02 表示的也是长度为2, 对应的 7676(0x76的10进制为118,对应ASCII码字符为v) 表示的是设置的值:vv
紧接后面的 FF 对应的是RDB_OPCODE_EOF,标识RDB文件结束了
最后的都是3545 3136 adb7 3b22
为8为校验码
AOF
最后看下AOF的文件格式,这次我们不用16进制打开,而是使用文本编辑器直接打开,内容如下
1 | ➜ cat appendonly.aof |
其实可以很明显的看到,前面的一段和RDB格式基本相同,而后面的其实就是我们执行的命令对应的RESP协议内容,这里就不再过多介绍了,不太了解的可以参考文档Redis Protocol specification, 或者我之前写的Redis服务端-客户端通信协议
内容到这里就介绍完了,如有错误,欢迎指正