Redis的设计与实现(3):持久化策略RDB、AOF

四、RDB持久化

RDF持久化就是将redis内存中的数据持久化到硬盘。
通过SAVE和BGSAVE命令可以创建RDB文件:

  • SAVE 命令会阻塞主进程,执行时客户端无法连接redis,等SAVE完成后,主进程才开始工作,服务端才可以处理命令。
  • BGSAVE 会fork一个SAVE的子进程,在执行SAVE过程中,不影响主进程,客户端可以正常连接服务端,等子进程fork执行save完成后,通知主进程,子进程关闭。尽管BGSAVE是非阻塞的,但是在BGSAVE执行过程中服务端对SAVE、BGSAVE和BGREWRITEOF命令还是有些许不同:
    - BGSAVE执行过程中,会拒绝客户端的SAVE命令,防止父子进程同时执行rdbSave,发生race condition。
    - BGSAVE执行过程中,会拒绝客户端的BGSAVE命令,同样是为了防止发生race condition。
    - BGSAVE和BGREWRITEOF不能同时执行:
    - BGSAVE正在执行时,那么BGREWRITEOF会延迟到BGSAVE执行完后执行。
    - BGREWRITEOF正在执行时,会拒绝执行BGSAVE。

创建RDB文件由rdbSave函数完成,SAVE和BGSAVE以不同的方式调用rdbSave,伪代码如下:
image.png
Redis没有专门的载入RDB文件的命令,在服务器启动时如果检测到RDB文件就会自动载入。因为AOF文件的刷新频率比RDB文件高,所以启动时还会检测是否开启了AOF功能。
image.png

设置间隔自动保存
redis可以通过save设置,让服务器每隔一段时间自动执行一次BGSAVE命令。例:
save 900 1
save 300 10
save 60 10000
满足任意一个条件都会执行自动保存,比如60s内发生了10000次修改。
如果没有主动设置,那么redis服务器会使用默认的save配置,其实就是上面这个配置。配置保存在redisServer结构的saveParams属性中。
此外,redisServer还维护了一个dirty计数器和lastsave属性,前者用来统计上一次SAVE或BGSAVE命令后服务器执行了多少次修改,后者是一个unix时间戳,用来记录上一次成功执行SAVE或BGSAVE命令的时间。
redis的周期性执行函数redisCron默认每100毫秒执行一次,执行时就会检测保存条件是否满足。

RDB文件结构
image.png
REDIS:长度5字节,保存"REDIS"5个字符(为书写方便,其实是5个单独字符),通过这个判断该文件是否为RDB文件。
db_version:长度4字节,是字符串表示的整数记录RDB的版本号。
database:包含0个或多个数据库及各数据库中键值对数据。如果长度为0字节,则表示无数据。
EOF:常量长度1字节,标志RDB文件正文的结束。读取时遇到该值,表示键值对的载入已经结束了。
check_sum:是一个8字节的无符号整数,保存一个同过前几位变量计算出来的校验和。每次加载都会进行计算校验,通过这个来判断文件是否损坏。

database部分细节
databases可以保存任意多个database,比如database0、database1,每个database对应一个非空数据库,每个非空数据库在RDB文件中都可表示为SELECTDB,db_number,key_value_pairs三部分

  • selectdb:1字节,标志位,标志着下一位存储的是数据库号码。
  • db_number:是一个数据库号码。
  • key_value_pairs:保存了数据库中所有键值对数据,如果有过期时间,则过期时间也会保存。

key_value_pairs结构
不带过期时间:
image.png
type:记录value的类型,长度1字节
key和value就是具体的键值对
而带过期时间的结构就是多了EXIRETIME_MS和ms,EXIRETIME_MS标记 程序接下来读取的是一个毫秒为单位的过期时间,ms是一个unix时间戳。
image.png

五、AOF持久化

RDB持久化是保存数据,而AOF持久化是保存执行命令,因为redis命令的请求协议是纯文本格式的,所以AOF文件可以直接打开。
AOF持久化功能的实现可以分为命令追加(append)、文件写入、文件同步(sync)三个步骤。

命令追加:服务器在执行完一个写命令后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾。

struct redisServer{
    //AOF缓冲区
    sds aof_buf;
    ...
};

Redis服务器进程就是一个事件循环,负责接收客户端命令请求及命令回复,时间事件负责执行像serverCron函数这样定时运行的函数。服务器每结束一个时间循环前,都会调用flushAppendOnlyFile函数,可用下面的伪代码表示:
image.png
考虑是否有必要将aof缓冲区中的内容写入和保存至AOF文件里。这个判断的依据就是根据配置文件的appendfsync值决定:

  • always:将aof_buf缓冲区的所有内容写入并同步到AOF文件。
  • everysec:将aof_buf缓冲区中的所有内容写入到AOF文件,如果上次同步AOF文件的时间超过一秒,就再次对AOF文件进行同步,并由一个线程专门负责。是默认设置。
  • no:将aof_buf缓冲区中的所有内容写入到AOF文件,但并不对AOF文件进行同步,何时同步由操作系统决定

文件的写入和同步:
写入≠同步,为提高写效率,用户调用write函数时,操作系统一般将写入数据暂时保存在内存缓冲区,等缓冲区填满或超过指定时间后才会真正地将数据同步到磁盘里。操作系统提供了fsync和fdatasync两同步函数,可强制操作系统同步数据,保证数据安全性。也就是说,每一次的事件循环,aof_buf中的指令都会被写入操作系统的缓冲区,根据appendfsync配置,当操作系统缓冲区满足一定条件后,就被真实地写入磁盘内。

appendfsync三种设置的安全性和效率:

  • always:最多损失一个事件循环中的数据,最安全,效率最慢
  • everysec:最多损失1s时间的数据。
  • no:最多损失上一次同步后的所有数据。

AOF文件的载入和还原
image.png
因为redis的命令只能在客户端的上下文环境中执行,但AOF是本地文件,所以要伪造一个没有网络连接的伪客户端。

AOF重写

AOF文件持久化是用过保存被执行的写命令,随着时间的流逝,会出现频繁的删除和创建键值对,或者对一个键的值频繁的更新,导致文件的内容越来越多。Redis提供AOF文件重写功能,让服务器创建一个新的AOF文件,替代现有的AOF文件,减少冗余命令。

AOF重写功能并不对AOF文件进行操作,而是直接读取数据库当前的数据状态。具体有由aof_rewrite函数实现,由于这个函数会执行大量的写操作,调用这个函数的线程会被长时间阻塞,而redis是用单线程来处理命令请求,所以如果由服务器直接调用这个函数,那么在AOF重写期间就不能响应客户端的请求,因此redis用一个子进程来执行重写操作,这样的好处有:

  • 父进程仍然可继续处理请求。
  • 子进程有自己的数据副本,而非子线程,可以避免一些线程安全性问题的出现。

子进程在执行AOF重写期间,服务器进程还需要继续处理命令请求,而新的命令可能会对现有的数据库状态进行修改,导致当前数据库状态与重写后的AOF文件保存状态不一致。为解决这个问题,设置了AOF重写缓冲区
当重写子进程创建后,Redis服务器执行完写命令就会将其写入AOF缓冲区和AOF重写缓冲区,子进程执行重写期间,服务器进程要执行3个工作:

  1. 执行客户端发来的命令。
  2. 将执行后的写命令追加到AOF缓冲区。
  3. 将执行后的写命令追加到AOF重写缓冲区。

image.png

当子进程完成重写后,会向父进程发送一个信号,父进程接收并调用信号处理函数,将重写缓冲区的所有内容写到新AOF文件中,原子地覆盖现有的AOF文件。因此整个AOF文件重写的过程中,只有信号处理函数执行时,才会阻塞,将性能损耗降到最低。

在重写时会先检查键所包含的元素数量,一个键有多个值时在命令转换时可能会导致客户端输入缓冲区溢出,因此默认元素数量超过64个就用多条指令记录。

在这里插入图片描述

重写命令可以手动调用也可以设置为自动触发:

#。当AOF文件大小的增长率大于该配置项时自动开启重写。
auto-aof-rewrite-percentage 100

#当AOF文件增长到一定大小的时候Redis能够调用 BGREWRITEAOF 对日志文件进行重写 。当AOF文件大小大于该配置项时自动开启重写
auto-aof-rewrite-min-size 64mb

注:内容是从语雀上的学习笔记迁移过来的,主要参考自《Redis的设计与实现》有些参考来源已经无法追溯,侵权私删。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值