2023-08-12
原文作者:Ressmix 原文地址:https://www.tpvlog.com/article/119

Redis作为分布式缓存,数据首先是存储在内存中的,但是为了保证数据不丢失,肯定需要将数据持久化到磁盘上。所以,本章我们就来讲讲Redis的持久化原理。

Redis一共提供了两种数据持久化方式: RDBAOF 。通过RDB或AOF,我们可以将Redis内存中的数据持久化到磁盘上,然后将这些数据备份。如果Redis挂了,可以将备份数据放到指定的目录中,然后重新启动Redis,Redis就会自动根据持久化数据文件中的数据,恢复到内存中,继续对外提供服务。

如果我们仅将Redis作为纯内存的缓存来用,那么可以禁止RDB和AOF。

一、RDB持久化

RDB持久化,是 一次性生成内存快照 的方式,也就是把当前Redis进程的数据生成快照,保存到磁盘上。触发RDB持久化的方式有手动触发和自动触发,生产环境一般都是自动触发,都是通过bgsave命令。

1.1 持久化原理

执行 bgsave 命令后,Redis主进程会执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短,整个过程如下图:

202308122220036641.png

  1. 执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如RDB/AOF子进程,如果存在则直接返回;
  2. 父进程执行fork操作创建子进程,fork操作过程中 父进程会阻塞
  3. 父进程fork完成后,bgsave命令返回“Background saving started”信息,并不再阻塞父进程,父进程可以继续响应其他命令;
  4. 子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换;
  5. 子进程发送信号通知父进程RDB持久化完成,父进程更新统计信息。

1.2 优点

  1. RDB会生成多个数据文件,每个数据文件都代表了某一个时刻中Redis的数据,这种多个数据文件的方式,非常 适合做定期的冷备
  2. RDB对Redis正常的读写服务影响非常小,可以让Redis保持高性能,因为Redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可;
  3. 相对于AOF持久化机制来说,直接基于RDB数据文件来重启和恢复数据更加快速。

1.3 缺点

  1. RDB持久化不能做到实时,因为频繁fork子进程的开销是很大的。所以如果想要在Redis故障时,尽可能减少数据丢失,那应该开启AOF。一般来说,RDB数据快照文件,都是每隔5分钟或者更长时间生成一次;
  2. RDB每次fork子进程生成数据快照文件时,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,甚至数秒。所以,我们一般会控制Redis实例的内存在10GB以内。

二、AOF持久化

针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式。
AOF(append only file)持久化,是对每条写入命令以append-only的模式写入一个AOF日志文件中,在Redis重启的时候,可以通过回放AOF日志中的写入指令来重新构建整个数据集。

持久化的核心原理,和多数开源分布式框架差不多,无非就是先写入os cache,然后定时刷盘。

2.1 持久化原理

Redis可以通过配置参数appendonly来开启AOF持久化。AOF持久化主要分为三个步骤:

  1. 命令追加(append)
  2. 文件写入(sync)
  3. 文件重写(rewrite)

整个流程如下图:

202308122220045062.png

命令追加

Redis的所有写入命令(以文本协议格式)会追加到aof_buf缓冲区的末尾,这个缓冲区其实就是操系统的filesystem cache,可以大幅提升磁盘IO的性能。

文件写入

AOF缓冲区的数据,会根据一定的策略写入到磁盘上的AOF日志文件中,具体有以下几种策略,由参数appendfsync控制:

1. always
每次写入AOF缓存后,立即调用操作系统的fsync命令,将数据刷到磁盘。一般不建议配置,因为会大幅降低吞吐量,除非对数据可用性有非常高的要求。

2. no
由操作系统自身去控制何时将os cache中的数据刷到磁盘上,同步周期不可控。一般也不建议配置。

3. everysec

每隔1秒钟(默认),调用操作系统的fsync命令将数据刷到磁盘。推荐配置。

文件重写

随着AOF日志文件越来越大,需要定期对AOF文件进行rewrite,以达到压缩的目的。
因为AOF会记录每一条写命令,所以会导致对于同一个Key,存在多个冗余命令。Redis会创建一个新的AOF文件来替换老文件,新旧两个文件所保存的数据状态完全相同,但是新文件不会包含任何浪费空间的冗余命令。

主进程通过bgrewriteaof命令进行重写:

  1. 父进程fork一个子进程;
  2. 子进程根据当前的内存快照,按照命令合并规则(避免命令冗余),写入到新的AOF文件;
  3. 在此期间,主进程会把接收到的新的写入命令追加到AOF缓存区(aof_buf),同时再写入一份到AOF重写缓存区(aof_rewrite_buf);
  4. 子进程完成新的AOF文件写入后,会发送信号给父进程;
  5. 父进程接受到信号后,把aof_rewrite_buf的数据写入到新的AOF文件中,这样新AOF文件中的数据状态就和当前数据状态是一致的了;
  6. 重命名新的AOF文件,覆盖掉老文件,完成AOF重写。

整个流程的示意图如下:

202308122220050893.png

2.2 优点

  1. AOF可以更好的保护数据不丢失,AOF默认每隔1秒,通过一个后台线程执行一次fsync操作,所以最多丢失1秒钟的数据;
  2. AOF日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易损坏,即使文件尾部破损,也很容易修复;
  3. AOF日志文件过大的时候,即使出现重写,由于是后台操作,所以也不会影响客户端的读写。
  4. AOF日志文件的命令以可读的方式进行记录,非常适合做灾难性的误删除操作的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令删除,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据。

2.3 缺点

  1. 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大;
  2. AOF开启后,支持的写QPS会比RDB低,因为AOF一般会配置成每秒fsync一次日志文件;
  3. AOF这种基于命令日志回放的方式,比RDB每次持久化一份完整数据快照的方式,更加脆弱一些,容易有bug。不过AOF就是为了避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据,进行指令的重新构建,这样健壮性会好很多。

三、总结

本章,我介绍了Redis进行数据持久化的两种方式RBD和AOF,并对它们的持久化原理做了深入分析。那么RDB和AOF到底该如何选择呢?事实上,它们两者并不是非此即彼的关系,生产环境一般会综合使用AOF和RDB,用AOF来保证数据不丢失,作为数据恢复的第一选择,用RDB来做周期型的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复。

阅读全文