redis的持久化
总述
本文章主要记录redis的两种持久化方式:RDB和AOF,知识来源于编程迷思大佬的博客和钱文品大佬的《Redis深度历险》,为两位巨人的知识创作点赞,如果本博客有表述不清楚的地方,可以前去这两个地方详细阅读;
持久化的功能:redis是内存型数据库,数据都存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将redis中的数据以某种形式(数据或者命令,分别对应两种持久化方式)从内存中保存到硬盘中去,当下次redis重启或者发生进程异常恢复的时候,利用持久化的文件就可以实现数据的恢复,除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置;下面分别介绍RDB持久化和AOF持久化方式;
RDB持久化
RDB持久化是将当前内存中的数据生成快照保存到硬盘中(也称之为快照持久化),生成的持久化文件的后缀是.rdb,当redis重启的时候,启动进程可以读取文件进行数据恢复;
1.触发条件
RDB持久化触发分为手动触发和自动触发两种,下面分别来详细了解一下rdb备份的两种触发方式
(1)手动触发
手动触发rdb的命令有:save/bgsave两种,因为save命令在执行rdb备份的同时会阻塞redis的主进程,直到rdb文件创建完毕为止,因此已经逐渐被废弃,在redis服务器的阻塞期间,不能够处理任何请求;
而bgsave其实会fork出一个子进程,由子进程来负责创建RDB文件,父进程则继续处理请求;需要注意的是父进程在fork子进程的阶段是阻塞的,也就是说fork子进程的过程中redis是不能继续响应客户端的请求的;
(2)自动触发
save m n
自动触发最常见的情况是在配置文件中配置 save m n, m和n这两个参数的意思是当m秒发生n次的redis数据库变更的时候,会触发RDB备份;
我们可以看一下redis.conf的配置文件里关于这个的配置:
可以看到这个配置是900秒内有1次变动或者300秒内10次变动或者60秒内10000次变动都会触发bgsave进而发生RDB数据备份,而且配置文件中的三个配置只要任意一个满足,就会发生bgsave的备份;
save m n的实现原理解析:
实现save m n 需要三个前置能力: serverCron + dirty计数器 + lastsave时间戳
可能我写到这里聪明的读者应该已经猜到了,serverCron可以理解是redis服务器的周期性操作函数,默认是100ms执行一次,该函数对服务器的状态进行维护,其中的一项工作就是检查save m n条件是否满足,如果满足就执行bgsave
dirty计数器顾名思义,其实是用来对redis数据库修改次数进行计数的,它记录了上次bgsave/save之后,服务器执行了多少次的增删改查,因为是自上次bgsave之后的,所以dirty再每次bgsave之后都会重置为0;
需要注意的是,服务器记录的是数据发生了多少数据的修改,而不是客户端提交了多少次修改,mset key1 value1 key2 value 2 也算两次的;
最后是lastsave的时间戳,记录的是上次成功执行save/bgsave的时间戳;
将这个三个能力串起来我们就可以得到save m n 的工作流程:
每间隔100ms,服务器的定时任务函数serverCron()就会执行,然后去检查save m n条件是否满足,具体检查的条件是: dirty >= n && currentTime - lastsave>= m 这样子;
ps:个人猜测 serverCron()函数是不是也用来处理键的过期时间? expire?
(3)其他触发机制
还有一些其他的case会触发bgsave:
- 主从复制场景下,如果从节点要求执行全量复制或者主节点发现只能进行全量复制,这时候主节点的redis主进程会fork子进程执行rdb的全量备份,然后将rdb文件发送给从节点
- redis执行shotdown的时候,会自动执行rdb备份进行持久化;
RDB的完整执行流程
看主进程的链路,fork出子进程之后就会继续去处理其他命令,fork的时候子进程会执行内存的copy,获得主进程fork时内存的全部的状态,然后子进程利用这部分内存的镜像来产生rdb的文件,copy完成之后会去通知主进程,整个流程执行完毕
但是这里有个问题,那就是在执行fork之后子进程产生rdb文件之前的客户端指令是没有办法被子进程感知到的,那这部分数据变更怎么处理呢?
RDB文件的文件格式
RDB文件是经过压缩的二进制文件,下面梳理一些rdb文件的相关细节;
存储路径
rdb文件的存储路径既可以在启动前配置,也可以通过命令来动态指定;
dir用来配置指定目录,dbfilename用来指定文件名;
RDB的文件格式
rdb文件格式如下图所示:
开头是REDIS五个字符常量,类似于.class文件中的cafebabe哈哈
然后是db_version,RDB文件的版本号,注意并不是redis的版本号
之后是SELECTDB 0 pairs;表示一个完整的数据库,redis数据库即使是单机的情况下,,仍然是分成了好多分的,类似于kafka中的分区的概念吧,如果一个指定号的数据库没有数据的话,是不会出现在rdb文件中的,所以我们可以看到只有0号数据库和3号数据库中有数据,也只有他们出现在redis的rdb文件中;
pairs中就是存储的具体的redis的键值对数据,EOF表示RDB文件的结束描述符,而check_sum是文件校验和;
启动时加载
RDB文件的载入工作是在服务器启动的时候自动执行的,并没有专门的命令,但是因为AOF的优先级更高,所以当AOF开启的时候,redis会优先载入AOF来恢复数据,只有当AOF关闭的时候,才会在Redis服务器启动时检测RDB文件并自动载入,服务器在载入rdb文件期间处于阻塞状态,直到载入完成为止;
RDB配置总结
本来这里我不打算记录了,但是考虑到后续工程中可能会着手去配置一些redis的相关的配置项,记录一下混个眼熟吧
- save m n:bgsave自动触发的条件;如果没有save m n配置,相当于自动的RDB持久化关闭,不过此时仍可以通过其他方式触发
- stop-writes-on-bgsave-error yes:当bgsave出现错误时,Redis是否停止执行写命令;设置为yes,则当硬盘出现问题时,可以及时发现,避免数据的大量丢失;设置为no,则Redis无视bgsave的错误继续执行写命令,当对Redis服务器的系统(尤其是硬盘)使用了监控时,该选项考虑设置为no
- rdbcompression yes:是否开启RDB文件压缩
- rdbchecksum yes:是否开启RDB文件的校验,在写入文件和读取文件时都起作用;关闭checksum在写入文件和启动文件时大约能带来10%的性能提升,但是数据损坏时无法发现
- dbfilename dump.rdb:RDB文件名
- dir ./:RDB文件和AOF文件所在目录
AOF持久化
aof是区别于rdb的完全不同的持久化方式,rdb的话是将内存中的数据进行快照化的存储,aof的话就是将客户端执行的每条指令进行持久化存储,两者是完全不同的两个思路;
先说结论:和RDB相比的话,AOF的实时性更好,因此已经成为了主流的持久化方案
有咩有想过为什么呢?
1.开启AOF
redis服务器默认开启RDB,关闭AOF,如果要开启AOF,需要在配置文件中配置
appendonly yes
2.执行流程
由于需要记录Redis的每条写命令,所以AOF不需要触发,AOF的主要执行流程包括下面三个阶段:
- 命令追加,就是持续将客户端的写入追加到aof_buf的缓冲区中
- 文件写入和文件同步;根据不同的同步策略将aof_buf缓冲区中的数据同步到磁盘中去;
- 文件重写,非必须,定期重写AOF文件,可以降低文件体积和执行耗时;
(1)命令追加
redis是将写命令追加到缓冲区中,而不是直接写入到文件,主要是为了避免每次有写命令的时候都直接写入到硬盘,导致磁盘io成为redis负载的平静;在AOF文件中,除了用于指定数据库的select命令(比如select 0 指的是选中0号数据库)是又redis添加的,其余的都是客户端发送过来的写命令;