Redis持久化(AOF|RDB)

AOF

AOF是什么?

AOF全称是append only file, 即只追加文件。

AOF持久化是通过保存Redis服务器执行的写命令来记录数据库状态的。

AOF默认是不开启的,,需要在redis.conf文件中配置:

1
2
appendonly yes
appendfilename "appendonly.aof"

AOF持久化配置项说明:

  • appendonly:是否开启AOF持久化
  • appendfilename:持久化文件名

redis处理写命令的时,会采取先写内存数据,再记录AOF日志的方式,这样做有两个好处:

  • 减少额外检查开销: 只有内存数据被修改成功后才会写AOF日志,保证了日志都是可执行且正确的
  • 不会阻塞当前的写操作

风险

如果命令执行完成,写日志之前宕机了,会丢失数据。

可能会阻塞下一条写命令的执行: 写操作和记录日志都是主进程(主进程中的redis_aof_write_thread线程),如果在刷盘时,服务器I/O压力过大,导致写硬盘速度很慢,就会阻塞后续的写命令

写AOF日志的过程

redis在执行完一个写命令之后,会以协议格式将被执行的写命令追加到日志缓存server.aof_buf中,接下来通过系统调用write(),将日志缓存server.aof_buf中的内容写入到AOF文件中。

有三种写回策略,需要通过设置redis.conf文件中appendfsync参数来配置:

AOF持久化配置项说明:

  • appendfsync:持久化策略
    • always:每个写命令执行完,立马同步地将日志写回磁盘,数据安全性最高,但性能最差
    • everysec:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,redis的后台异步线程每隔一秒把缓冲区中的内容写入磁盘
    • no:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘

AOF重写

AOF重写是指对AOF文件进行压缩,只保留可以恢复数据的最小命令集合,可以有效减小AOF文件的体积,以提升数据恢复的性能。

重写触发的条件:

  • auto-aof-rewrite-min-size:表示运行AOF重写时文件的最小大小,默认为64MB。
  • auto-aof-rewrite-percentage:这个值的计算方式是,当前aof文件大小和上一次重写后aof文件大小的差值,再除以上一次重写后aof文件大小。也就是当前aof文件比上一次重写后aof文件的增量大小,和上一次重写后aof文件大小的比值。

流程:读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到新的 AOF 文件,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件

为什么要新建一个AOF文件?
如果在原先的AOF文件进行重写,如果重写失败,就会出现数据不一致,导致污染,可能无法用于恢复

为什么是后台子进程而不用子线程?
子线程会共享内存,主进程要写,子线程要读,要加锁防止读写并发操作,性能差

采用子进程,写时复制提高性能:
采用子进程,主进程会为子进程分配与主进程相同的页表,即虚拟空间映射到相同的物理空间,达到数据共享,并且不需要加锁操作.但会对这块共享的物理内存设置为只读状态,如果主进程 或 子进程 对这块内存发生写操作,会触发写时复制.CPU 触发写保护中断,这个写保护中断是由于违反权限导致的,然后操作系统会在「写保护中断处理函数」里进行物理内存的复制,并重新设置其内存映射关系,将父子进程的内存读写权限设置为可读写,最后才会对内存进行写操作,这样就避免了加锁操作,通过写时复制,来提高性能.(简单来说就是如果主进程在子进程进行AOF重写时,没有执行写入操作,就不会真正分配内存资源,就是写时复制技术COW的应用)

AOF后台重写机制描述

进行AOF日志后台重写时,主进程fork子进程,并且为子进程分配与主进程相同的页表,即虚拟空间映射到相同的物理空间,达到数据共享,并且通过写时复制,来避免加锁

子进程收集内存中的键值对转化为一条命令,写入新的AOF文件,但在写入过程,主进程仍然可以处理命令,当碰到写操作命令时,会发生以下操作:
触发写时复制过程,CPU 触发写保护中断,这个写保护中断是由于违反权限导致的,然后操作系统会在「写保护中断处理函数」里进行物理内存的复制,并重新设置其内存映射关系,将父子进程的内存读写权限设置为可读写,最后主进程才会对内存进行写操作

但子进程收集的AOF文件 数据就不一致了,如何解决?

redis 在AOF后台重写触发时,会分配一个AOF重写缓冲区,重写过程,主进程执行更新内存数据时,需要执行以下几件事情:

  1. 执行命令,更新内存数据
  2. 将命令记录到两个位置:旧的AOF文件和AOF重写缓冲区
  3. 当子进程 AOF重写完毕,会发送一个信号告诉主进程,主进程调用信号处理函数会做以下事情:将AOF重写缓冲区的数据直接追加到新的 AOF文件末尾,替换旧的AOF文件,重写过程就结束。信号处理函数执行完毕,主进程就可以继续处理命令了

后台重写,什么阶段会阻塞主进程?

  1. 创建子进程的途中,由于要复制父进程的页表等数据结构: 阻塞的时间跟页表的大小有关,页表越大,阻塞的时间也越长
  2. 创建完子进程后,如果父进程修改了共享数据(子进程一般只读),就会发生写时复制,这期间会拷贝物理内存,如果内存越大,自然阻塞的时间也越长
  3. 子进程 AOF重写完毕,发送信号通知主进程,主进程会调用信号处理函数,执行时也会对主进程造成阻塞

什么优化,让主进程阻塞时间尽量缩小?
写时复制,在发生写操作的时候,操作系统才会去复制物理内存,这样是为了防止 fork 创建子进程时,由于物理内存数据的复制时间过长而导致主进程长时间阻塞的问题。当冲突发生,再进行解决,来提高效率。也可以减少内存分配。

RDB

RDB快照是什么?如何生成?

RDB 快照记录的是内存中的实际数据,二进制格式;所以在redis重启恢复数据时,只需要加载rdb文件到内存即可,速度较快

redis提供了两个命令来主动生成rdb文件
●save:主进程来执行全量快照操作,若内存数据太多,会阻塞主进程,不推荐
●bgsave:主进程fork 子进程,来执行全量快照操作,这种思想在AOF后台重写日志也用过,避免主线程的阻塞

还可以通过配置参数,来生成快照
默认配置:执行的是 bgsave 命令,也就是会创建子进程来生成 RDB 快照文件。

1
2
3
save 900 1
save 300 10
save 60 10000

只要满足上面条件的任意一个,就会执行 bgsave,它们的意思分别是:
900 秒之内,对数据库进行了至少 1 次修改;
300 秒之内,对数据库进行了至少 10 次修改;
60 秒之内,对数据库进行了至少 10000 次修改。

执行RDB快照过程是怎样的?执行快照时,数据能被修改吗?

Redis 同样采用写时复制技术,来支持快照,执行快照时数据能被修改,具体过程:

主进程fork创建一个子进程,来执行bgsave命令,主进程会将自己的页表复制一份给子进程,虚拟空间指向相同的物理空间,来达到空间共享.子进程在记录实际数据到RDB文件过程中,若主进程没有执行写操作命令,那么共享空间就不会发生冲突,也不会发生写时复制。

若主进程需要执行写操作命令,如主线程(父进程)要修改共享数据里的某一块数据(比如键值对 A)时,就会发生写时复制,这块数据的物理内存就会被复制一份(键值对 A’),然后主线程在这个数据副本(键值对 A’)进行修改操作。与此同时,bgsave 子进程可以继续把原来的数据(键值对 A)写入到 RDB 文件。

bgsave 快照过程中,如果主线程修改了共享数据,发生了写时复制后,本次RDB 快照保存的是原本的内存数据,而主线程刚修改的数据,是没办法在这一时间写入 RDB 文件的,只能交由下一次的 bgsave 快照

怎么将RDB文件加载到内存?

RDB 文件的加载工作是在redis服务器启动时自动执行的,Redis 并没有提供专门用于加载 RDB 文件的命令。

RDB 快照的缺点
每一次执行RDB快照,记录的是内存中的所有数据,相当于一个全量快照,当数据较多时,频繁记录快照,虽然可以采用子进程来执行,但还是会影响redis的性能,如果记录的太不频繁,那么当服务器宕机,丢失的数据就非常多

写时复制有什么极端场景?

在 Redis 执行 RDB 持久化期间,刚 fork 时,主进程和子进程共享同一物理内存,但是途中主进程处理了写操作,修改了共享内存,于是当前被修改的数据的物理内存就会被复制一份。那么极端情况下,如果所有的共享内存都被修改,则此时的内存占用是原先的 2 倍。所以,针对写操作多的场景,我们要留意下快照过程中内存的变化,防止内存被占满了。

AOF日志 & RDB快照 区别

它们都是redis数据库内存持久化的技术

  1. 记录的内容不同:AOF 文件的内容是操作命令;RDB 文件的内容是二进制数据。
  2. 恢复数据时,耗费的时间不同:RDB 恢复数据的效率会比 AOF 高些,因为直接将 RDB 文件读入内存就可以,AOF 需要额外执行操作命令的步骤才能恢复数据,速度慢
  3. 在服务器发生故障时,丢失数据多少方面:RDB是一个全量快照,不能频繁执行,否则会影响性能,所以丢失数据相对来说较多.AOF支持秒级记录命令到AOF文件中,所以丢失的数据相对来说较少

AOF|RDB混合持久化

AOF|RDB进行混合持久化有什么优点?

混合持久化可以利用这两种日志的优点:AOF可以支持秒级记录日志,使得数据更少的丢失,RDB在数据恢复时非常快,不需要重新执行命令

混合持久化的过程只发生在AOF日志重写的过程,如下:

  1. 当AOF日志达到阈值时,触发后台重写机制,主进程fork创建子进程,并且将主进程的页表复制一份给子进程
  2. 子进程与主进程共享空间,对当前内存数据执行RDB快照,将RDB格式的日志记录到新的AOF文件中
  3. 并且在重写过程,主线程处理的写操作命令会被记录在AOF重写缓冲区里,当RDB快照记录完毕,AOF重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件
  4. 写入完成后通知主进程,将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。

使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。

好处
重启Redis加载数据的时候,由于前半部分是RDB内容,这样加载的时候速度会很快。加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失。