Redis-持久化
一、RDB持久化
为了解决服务器进程退出导致数据丢失,Redis提过了RDB持久化功能,可以将Redis在内存中的数据保存到磁盘中,避免数据意外丢失。
RDB持久化既可以手动执行,也可以根据服务器配置选项定期执行,该功能将某个时间点上的数据库状态保存到一个RDB文件中。生成的RDB文件是一个经过压缩的二进制文件,通过该文件,可以还原生成RDB文件时的数据库状态。
1、RDB文件的创建与载入
命令:SAVE、BGSAVE
SAVE 命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求。
BGSAVE 命令会派生一个子进程,然后由子进程负责创建RDB文件,服务器进程继续处理命令请求。
创建RDB文件的实际工作由rdb.c/rdbSave函数完成,SAVE命令和BGSAVE命令会以不同的方式调用这个函数。
RDB文件的载入工作是在服务器启动时自动执行,只要Redis服务器在启动时检测到RDB文件存在,就会自动载入RDB文件。
因为AOF文件更新频率通常比RDB文件更新频率高,所以:
1)如果服务器开启了AOF持久化公共能,那么服务器会优先使用AOF文件来还原数据库状态
2)只有在AOF持久化功能关闭状态时,服务器才会使用RDB文件来还原数据库状态
当SAVE命令执行时,Redis服务器会被阻塞,客户端发送的所有命令请求都会被拒绝
当BGSAVE命令执行时,Redis服务器任然可以继续处理客户端命令请求,但是,在BGSAVE命令执行期间,服务器处理SAVE、BGSAVE、BGREWRITEAOF三个命令的方式会和平时有所不同。
1)在BGSAVE命令执行期间,客户端发送的SAVE命令会被服务器拒绝
2)在BGSAVE命令执行期间,客户端发送的BGSAVE命令会被服务器拒绝
3)BGREWRITEAOF和BGSAVE命令不能同时执行:如果BGSAVE 命令正在执行,BGREWRITEAOF命令会被延迟到BGSAVE命令执行完毕之后执行;如果BGREWRITEAOF命令正在执行,BGSAVE命令会被服务器拒绝
服务器载入RDB期间,会一直处于阻塞状态。
2、自动间隔性保存
因为BGSAVE命令可以在不阻塞服务器进程的情况下执行,所以Redis允许用户通过设置服务器配置的save选项,让服务器每隔一段时间自动执行BGSAVE命令。
用户可以通过save选项设置多个保存条件,只要其中一个条件被满足,服务器就会执行BGSAVE命令。
2.1、设置保存条件
当服务器启动时,用户可以通过指定配置文件或传入参数的方式设置save选项,如果用户没有主动设置save选项,服务器默认设置
save 900 1 (服务器在900秒之内,对数据库进行了至少1次修改)
save 300 10 (服务器在300秒之内,对数据库进行了至少10次修改)
save 60 10000 (服务器在60秒之内,对数据库进行了至少10000次修改)
服务器程序会根据save选项所设置保存条件,设置服务器状态redisServer的saveparams属性。
2.2、dirty计算器和lastsave属性
dirty计数器 记录距离上一次成功执行SAVE或BGSAVE命令之后,服务器对数据库状态进行了多少次修改(包括写入、删除、更新操作)
lastsave属性 是一个UNIX时间戳,记录服务器上一次成功执行SAVE命令或者BGSAVE命令的时间
2.3、检查保存条件是否满足
Redis的服务器周期性操作函数serverCron默认100毫秒执行一次,该函数用于对正在运行的服务器进行维护,其中一项工作就是检查save选项所设置的保存条件是否满足,如果满足,就执行BGSAVE命令。
3、RDB文件结构
RDB文件最开头是REDIS部分,这个部分的长度为5字节,保存"REDIS"五个字符。通过这5个字符,判断载入的文件是否RDB文件。
db_version长度4字节,是一个字符串表示的整数,记录RDB文件的版本号
databases部分包含零个或任意多个数据库,以及各个数据库中的键值对数据:
1)如果服务器的数据库状态为空,则这个部分为空,长度为0字节
2) 如果服务器的数据库状态为非空,那么这个部分也为非空,根据键值对数量、类型和内容不同,长度也会有所不同
EOF常量长度为1字节,标志RDB文件正文内容结束
check_sum是一个8字节的无符号整数,保存一个校验值,是通过对REDIS、db_version、databases、EOF四个部分内容计算得到,用来检查文件是否损坏。
databases部分
一个RDB文件的databases部分可以保存任意多个非空数据库,每个非空数据在RDB文件中都可以保存SELECTDB、db_number、key_value_pairs三部分
SELECTDB常亮长度1字节,标识数据库结构的开始
db_number保存数据库号码,可以是1字节、2字节、5字节。当读入db_number部分后,会调用SELECT命令,切换到相应数据库
key_value_pairs部分保存数据库中所有的键值对数据,如果键值对带有过期时间,则过期时间和键值对保存在一起。
key_value_pairs部分
不带过期时间的键值对由 TYPE、key、value三部分组成
带有过期时间的键值对 由EXPIRETIMES_MS、ms、TYPE、key、value组成
EXPIRETIMES_MS 常量,1个字节、标志接下来是一个带过期时间的键值对,单位毫秒
ms 8字节带符号整数,记录一个以毫秒为单位的UNIX时间戳,表示键值对的过期时间
value编码
value部分保存一个值对象
二、AOF持久化
Redis还提供了AOF持久化功能,AOF通过保存Redis服务器所执行的写命令来记录数据库状态
1、AOF持久化的实现
AOF持久化实现可以分为命令追加(append)、文件写入、文件同步(sync)三个步骤
1.1、命令追加
当AOF持久化工能处于打开状态时,服务器在执行一个写命令后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓存区的末尾
1.2、AOF文件的写入与同步
Redis服务进程就是一个事件循环(loop),这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送的命令回复,
因为服务器在处理文件事件时可能执行写命令,使得一些内容追加到aof_buf缓存区,所以在服务器结束一个事件循环之前,会调用flushAppendOnlyFile函数,考虑是否需要将aof_buf缓冲区中的内容写入和保存到AOF文件。
flushAppendOnlyFile函数的行为有服务器配置的appendfsync选项决定
如果用户没有主动设置appendfsync选项,默认为everysec。
2、AOF文件的载入与数据还原
Redis读取AOF文件并还原数据库状态的步骤:
1)创建一个不带网络连接的客户端
2)从AOF文件中分析并读取一条写命令
3)使用步骤1创建的伪客户端执行步骤2读取的写命令
4)重复执行步骤2和步骤3,直到AOF文件中所有写命令都处理完毕。
3、AOF重写
为了解决AOF文件体积膨胀的问题,Redis提供了AOF重写(rewirte)功能,通过该功能,可以创建一个新的AOF文件替代现有的AOF文件
3.1 、AOF重写的实现
因为aof_rewrite函数生成的新AOF文件只包含还原当前数据库所必须的命令,所以新AOF文件不会浪费硬盘空间
3.2、AOF后台重写
服务器直接调用aof_rewrite函数,则无法处理客户端发来的命令请求。
为了解决后台重写数据不一致的问题,Redis服务器设置了一个AOF重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用,当Redis服务器执行一个写命令之后,会同时将这个命令发送给AOF缓冲区和AOF重写缓冲区。