首页 > 编程知识 正文

redis持久化rdb和aof工作原理,redis aof持久化

时间:2023-05-03 14:14:08 阅读:203970 作者:4511

我们知道关系型数据库比如MySQL支持全备、差备、增备。为了保证Redis故障重启后仍然可用我们的Redis支持全备(RDB快照备份)和增备(AOF日志连续增量备份),下面我们就来解读Redis持久化的原理。

RDB基础知识

RDB文件存在是以一个压缩后的二进制文件,这个RDB文件一般是保存在Redis安装目录下,通过启动Redis服务器执行rdbLoad函数加载RDB文件,执行rdbSave函数保存RDB文件。

RDB保存

rdbSave函数负责将内存中的数据库数据以RDB格式保存到磁盘中,如果RDB文件已存在,那么新的RDB文件将替换已有的RDB文件,SAVE和BGSAVE两个命令都会调用rdbSave函数。
SAVE直接调用rdbSave,阻塞Redis主进程,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。
BGSAVE则开启一个子进程,子进程负责调用rdbSave,Redis服务器在BGSAVE执行期间仍然可以继续处理客户端的请求,并在保存完成之后向主进程发送信号,通知保存已完成。

注意:执行保存命令时Redis不会再次执行save、bgsave、bgrewriteaof等命令,因为如果执行这些命令会存在资源抢占使备份出现意想不到的bug。

自动隔离保存

此时我们的小伙伴意识到,我们不能每次都执行命令手动保存或者执行定时器执行命令,这样会导致可用性降低,那么Redis提供自动保存的配置

####100秒内对Redis进行10次以上的修改save 100 10####一小时内对Redis进行至少1000次的修改save 3600 1000###只要满足上面任何一个要求则执行bgsave命令

那么Redis源代码中保存这个配置的是在redisServer中的一个saveparam的结构,代码如下:

struct saveparam {    teme_t seconds;  //秒    int changes; //修改数}

RDB快照原理

save我们这里就不分析了,因为它直接阻塞主线程把Redis数据直接写入磁盘文件,所以我们这对bgsave进行讲解,前面说了bgsave是开启一个子进程进行文件的操作,那么此时的Redis主进程还在响应客户端的命令,那么我们的Redis如何保证数据完整性和性能呢?

Redis在持久化的时候会fork产生一个子进程,持久化过程完全交给子进程,而父进程则响应客户端的命令,子进程创建时不会立即保存文件因为这会与父进程产生资源抢占,它的指针指向父进程的内存空间与父进程共享内存,这样为了更加节约资源。

此时父进程是会读写内存中的数据,子进程只是读取资源并不会写,此时Redis使用操作系统的多进程COW(Copy On Write)机制进行数据段页面的分离,每页数据不会超过4K,我们把这个数据称之为冷数据,父进程进行修改的时候我们只需要把共享的页数据复制出来对其进行修改,而不在原数据上进行修改,冷数据没有任何的变化。子进程现在可以安心的对冷数据进行持久化,此时我们持久化的内容就像是被拍了照一样固定下来,这就是快照的整个过程。

这里值得注意的是随着时间的推移,父进程修改的页也会越来越多,理论上这个复制出来的内存能达到之前数据内存,所以我们在设计缓存的时候尽可能的少改数据。

AOF日志原理

Redis还有一个持久化操作那就是AOF(Append Only File),AOF是将客户端的修改指令保存下来追加到服务器状态的aof_buf缓冲区尾部,最后保存到AOF日志文件。举个例子。

首先,前面我们讲过Redis的通讯协议RESP,我们这里把客户端的指令转换成这种协议

>set name mango####保存到aof_buf缓冲区*3rn$3rnsetrn$4rnamern$5rmangorn

然后我们将协议文本追加到aof_buf末尾

最后我们把这些指令信息保存至AOF日志文件中

AOF保存模式

每当服务器常规任务函数被执行、或者事件处理器被执行时,aof.c/flushAppendOnlyFile函数都会被调用,这个函数执行以下两个工作:
WRITE:根据条件,将aof_buf中的缓存写入到AOF文件。
SAVE:根据条件,调用fsync或fdatasync函数,将AOF文件保存到磁盘中。

Redis目前支持三种AOF保存模式:
1. AOF_FSYNC_NO:不保存,操作系统来决定什么时候保存保存。
2. AOF_FSYNC_EVERYSEC:每一秒钟保存一次。
3. AOF_FSYNC_ALWAYS:总是保存,每执行一个命令保存一次。

这三种模式从上到下执行速度越来越慢,安全性越来越高,如果使用no模式,那么恢复数据会恢复到上次备份,如果是everysec最多会丢失一秒,而always只会丢失一个指令。所以我们考虑使用哪种AOF模式取决于我们的业务需求综合考虑。

关于fsync

AOF日志是以文件的形式存在的,当我们的AOF从缓冲区写入AOF文件中时服务器突然宕机,此时我们的文件还没有完全写入磁盘,这时我们该如何处理呢?
fsync函数可以将指定文件的内容强制从内核缓存刷到磁盘。只要Redis进程实时调用fsync函数就可以保证AOF日志不失。但是fsync是一个磁盘IO操作,so它很慢。如果Redis执行一条指令就要fsync一次,那么可想而知Redis的性能就会拉下一大截 。
所以在生产环境的服务器中,Redis通常是每隔1s左右执行一次fsync操作,这个1s的周期是可以配置的。这是在数据安全性和性能之间做的一个折中,在保持高性能的同时,尽可能使数据少丢失。
Redis同样也提供了另外两种策略,一个是永不调用fsync让操作系统来决定何时同步碰盘,这样做很不安全,另一个是来一个指令就调用fsync一次——结果导致非常慢。这两种策略在生产环境中基本不会使用。

AOF重写

我们的AOF一直累加进行持久化,随着时间的推移备份文件会越来越大,甚至影响我们对Redis的恢复和操作,Redis也提供重写机制,下面我们来看看

>set name zhangsan>set name mango>set name lisi...>set name mango####优化后>set name mango

我们可以看到经常会对一个key进行多次修改,那么我们可以把这个key的最后一次操作保存起来这样我们就轻易的给AOF"瘦身"。当然我们还有一种方式,就是遍历整个Redis,set每个key和它的值,也跟RDB全备一样我们需要一个子进程读取当前的Redis库。

这里会出现一个问题,我们如果是遍历整个Redis需要考虑此时的客户端必定会有指令更改里面的值,此时我们怎么保证AOF重写后不丢下重写后的指令呢?

操作步骤:

AOF创建一个子进程进行AOF重写,其指定内存跟主进程一致

客户端执行写命令,主线程处理指令,指令追加到AOF缓冲区,并且追加到AOF重写缓冲区

AOF重写完成后替换现有的AOF文件

那么为什么会把这个指令同时追加到AOF缓冲区和AOF重写区呢?原因是如果我们在重写的时候突然服务器挂了,那么我们AOF文件中会保存这个指令。追加到AOF缓冲区是为了保证操作指令能及时同步到AOF重写区。AOF重写操作也就是之前提到过的bgrewriteaof。

Redis4.0混合持久化
Redis服务重启时,RDB会还原Redis库,那么如果Redis设置了AOF日志备份,Redis优先使用AOF恢复Redis库。为什么可以使用AOF恢复呢?原因是因为AOF是一个增量备份,按照AOF的规则我们可以恢复比较全的数据。当然RDB的恢复速度是比AOF快的,比较它是一个压缩后的二进制文件,相对于执行命令来说的确快。

Redis4.0为了解决这个问题,带来了一个新的持久化选项一一混合持久化。将RDB文件的内容和增量的AOF日志文件存在一起。这里的AOF日志不再是全量的曰志,而是自持久化开始到持久化结束的这段时间发生的增量AOF日志,通常这部分AOF日志很小。

在Redis重启的时候,可以先加载RDB,然后再重放增量AOF日志,就可以完全替代之前的AOF全量文件重放,重启效率因此得到大幅提升。

 

总结一下:

Redis持续化操作包括RDB和AOF

RDB保存的是一个压缩二进制文件,AOF保存的是操作指令文件

Redis优先恢复AOF,因为它比较全,Redis4.0采用混合模式来恢复

RDB执行bgsave时和AOF重写一样,开启一个子进程,他们的内存与父进程共享

AOF有三种保存模式,no不主动保存依赖操作系统调度;everysec一秒执行一次;always每次执行一个指令保存一次。

AOF保存模式主要是fsync函数,保证AOF日志不会丢失

 

 

一名正在抢救的coder

笔名:mangolove

CSDN地址:https://blog.csdn.net/mango_love

GitHub地址:https://github.com/mangoloveYu

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。