- RDB
- 命令
- SAVE
- BGSAVE
- BASAVE的自动定时保存
- save配置
- 执行原理:RedisServer中的saveparams,dirty,lastsave属性
- RDB文件结构
- AOF
- AOF与RDB区别
- AOF实现原理(三个写入策略)
- AOF载入
- AOF重写
- AOF后台重写
1. RDB持久化
什么是RDB持久化:由于内存的不稳定,RDB持久化将Redis在内存中的数据保存到磁盘里。这个过程既可以手动执行,也可以根据服务器配置定期执行
(1)命令
有两个Redis命令可以用于生成RDB文件:SAVE和BGSAVE
- SAVE:阻塞进程,客户端的所有命令都会被拒绝
- BGSAVE:不阻塞,fork()子进程进行持久化
原理:两者都是由rbdSave函数,如果没有开启AOF,开启服务器自动载入RDB文件。如果开启了AOF持久化,会优先使用AOF文件来还原数据库
(2)持久化指令的冲突问题
- 在SAVE期间
- 执行BGSAVE,BGSAVE会被延迟执行
- 在BGSAVE的执行期间,服务器处理SAVE,BGSAVE,BGREWRITEAOF三个命令的方式会不同
- SAVE指令会被拒绝
- BGSAVE指令也会被拒绝
- BGSAVE和SAVE的底层都是rdbSave函数,同时进行会产生竞争
- BGREWRITEAOF指令会被延迟到BGSAVE结束之后再执行
- 在BGREWRITEAOF的执行期间,调用BGSAVE会被服务器拒绝
(2)BGSAVE的自动定时保存
- save的配置
- 通过设置服务器conf的save选项,服务器每隔一段时间自动执行一次BGSAVE命令。
- 如果用户没有自己设置save选项,服务器会采用以下默认设置
//只要满足以下条件的任意一个,BGSAVE就会被执行
//服务器在900秒内,对数据库至少进行了1次修改
//服务器在300秒内,对数据库至少进行了10次修改
//服务器在60秒内,对数据库至少进行了10000次修改
save 900 1
save 300 10
save 60 10000
- 实现原理
- redisServer中的saveparams结构记录了save的设置;
- redisServer中有个dirty计数器,每次成功执行修改命令,更新dirty++;
- lastsave保存了上次SAVE或者BGSAVE的时间戳(比如1378270800)
- serverCron函数每隔100毫秒会执行一次,观察条件是否满足(满足任意一个saveparams中的条件,则进行BGSAVE持久化)
- dirty和时间戳 是否同时满足 saveparams里面的条件
struct redisServer{
//记录了保存条件的数组
struct saveparam *saveparams;
//修改计数器
long long dirty;
//上一次执行保存的时间
time_t lastsave;
}
struct saveparam {
//秒数
time_t seconds;
//修改数
int changes;
}
(3)RDB文件结构
- REDIS:5字节,RDB文件以REDIS开头,就像Class文件的BABYCAFE
- db_version:4字节,记录文件版本号。如0006表示第六版
- databases
- 如果数据库为空,则这一部分长度为0字节
- 如果非空:格式为
- SELECTDB
- 数据库编号
- (第一个KV),(第二个KV).....,(最后一个KV)
- EOF:1字节,结束符,代表正文内容结束。
- check_sum:8字节的无符号整数,校验和,用于文件是否损坏
2. AOF持久化
- AOF与RDB区别
- 比如执行三个命令(1)SET msg "hello"(2)SADD fruits "apple" "banana" "cherry"(3)RPUSH numbers 128 256 512
- RDB:保存数据库状态,将msg, fruits, numbers三个KV键值对保存到文件
- AOF:保存的是服务器执行的写命令
- AOF文件格式如下
- AOF写入实现原理:分为命令追加、文件写入、文件同步三个步骤
- 命令追加(append)
- 命令追加至缓冲区:开启AOF持久化后,服务器每次执行写命令后,都会加到aof_buf缓冲区的末尾
-
struct redisServer{ //AOF缓冲区 sds aof_buf; }
- 文件写入和文件同步(fsync)
- Redis服务器进程是一个事件循环(loop),在每次结束一个事件循环前,都会调用flushAppendOnlyFile函数,根据appendfsync的值考虑是否将aof_buf缓冲中的值写入和保存到AOF文件中
- 备注:write();fsync();fdatasync()
- write():现代操作系统中,当用户调用wirte函数,os会先写入osbuffer,等osbuffer空间满了再一起写入磁盘。提高了效率,但是会引起丢失
- fsync()和fdatasync():因此,系统提供了这两个函数,可以强制让os把osbuffer的数据持久化到磁盘,保证安全性
- 备注:write();fsync();fdatasync()
- 三种策略(appendfsync的值)
- Redis服务器进程是一个事件循环(loop),在每次结束一个事件循环前,都会调用flushAppendOnlyFile函数,根据appendfsync的值考虑是否将aof_buf缓冲中的值写入和保存到AOF文件中
- 命令追加(append)
appendsync的值 | flushAppendOnlyFile函数的行为 | 性能 |
always | 写入osbuffer:每次 同步fsync:每次 | 性能最慢,但最安全 |
everysec(默认) | 写入osbuffer:每次 同步fsync:间隔1秒 | 性能足够快,容忍一秒内容的丢失 |
no | 写入osbuffer:每次 同步fsync:由操作系统决定 | 最快,但是单次同步事件最长 易丢失,总体和everysec差不多 |
- AOF载入
- AOF重写
- 随着AOF持久化,文件会越来越大,还原数据的事件也会越来越多,但是其中很多命令是冗余的。因此提供重写功能。
- 实现原理:遍历所有数据库,遍历所有键,记录命令。
- 注:(1)已过期的数据不记录;(2)一条指令最多写64个元素
- eg1. 数据库0中有key= animals; value = 'dog', 'cat', 'panda'。 则写入 SADD animals dog cat panda指令
- AOF后台重写
- 如同BGSAVE,也开启子进程进行后台重写。但是在后台重写的过程中,会有新指令。
- 解决方法:设置AOF重写缓冲区,缓冲区在进行后台重写时开启。重写期间每次有新命令同时发送到AOF缓冲区和重写缓冲区。
- 当子进程的AOF重写完场后,它会向父进程发送signal,父进程收到后会调用信号处理函数,信号处理函数将重写缓冲区的内容合并至文件中。
- 性能问题
- 在整个过程中,只有信号处理函数(向文件追加aof重写缓存区的数据)时,会阻塞父进程。性能很高