跳至主要內容

Redis持久化

xw大约 7 分钟数据库Redis

概述

Redis持久化支持RDB和AOF两种方式,两种方式各有优劣。在4.x以后支持RDB和AOF混合使用。

RDB

RDB保存某一个时间点之前的数据。

配置

通过配置参数,例如在配置文件中写入如下配置:

 save 601000

还可以在客户端使用bgsave命令执行。

使用info命令可以查看RDB和AOF执行情况:

image-20220921112217426

相关解释如下:

    # Persistence
    loading:0                            //是否正在加载RDB文件内容
    rdb_changes_since_last_save:2     //最后一次保存之后改变的键的个数
    rdb_bgsave_in_progress:0           //是否正在后台执行RDB保存任务
    rdb_last_save_time:1540371552     //最后一次执行RDB保存任务的时间
    rdb_last_bgsave_status:ok          //最后一次执行RDB保存任务的状态
    rdb_last_bgsave_time_sec:0         //最后一次执行RDB保存任务消耗的时间
    rdb_current_bgsave_time_sec:-1    //如果正在执行RDB保存任务,则为当前RDB任务已经消耗的时间,否则为-1
                                       
    rdb_last_cow_size:6631424          //最后一次执行RDB保存任务消耗的内存
    aof_enabled:0                        //是否开启了AOF功能
    aof_rewrite_in_progress:0          //是否正在后台执行AOF重写任务(重写在后续的章节介绍)
    aof_rewrite_scheduled:0            //是否等待调度一次AOF重写任务。如果触发了一次AOF重写,
                                       //但是后台正在执行RDB保存任务时会将该状态置为1
                                       
    aof_last_rewrite_time_sec:-1      //最后一次执行AOF重写任务消耗的时间
    aof_current_rewrite_time_sec:-1   //如果正在执行AOF重写任务,则为当前该任务已经消耗的时
                                        间,否则为-1
    aof_last_bgrewrite_status:ok      //最后一次执行AOF重写任务的状态
    aof_last_write_status:ok           //最后一次执行AOF缓冲区写入的状态(服务端执行命令时会开
                                        辟一段内存空间将命令放入其中,然后从该缓冲区中同步到文
                                        件。该状态标记最后一次同步到文件的状态)
    aof_last_cow_size:0                 //最后一次执行AOF重写任务消耗的内存

执行流程图如下:

image-20220921111915031

优缺点

优点:

  • RDB数据格式紧凑,是某个时间点的全量备份
  • RDB非常适合于灾难恢复,它是一个可以传输到远端数据中心的紧凑文件
  • 在大数据量情况下RDB恢复速度比AOF更快
  • RDB只需要把相应数据加载到内存并生成相应的数据结构(有些结构如intset、ziplist,保存时直接按字符串保存,所以加载时速度会更快)。

缺点:

  • RDB可能会丢失数据,比如5分钟进行一次RDB快照操作,如果Redis停止工作而没有正确关闭的情况下,会丢失相关数据。
  • RDB需要fork操作,以便使用子进程在磁盘上持久化。如果数据集很大,fork操作可能会很耗时,如果数据集很大,CPU性能不太好,可能会导致Redis停止服务客户端几毫秒甚至一秒。AOF也需要fork(),但频率较低,可以调整想要重写日志的频率。

AOF

AOF是Redis的另外一种持久化方式。简单来说,AOF就是将Redis服务端执行过的每一条命令都保存到一个文件,这样当Redis重启时只要按顺序回放这些命令就会恢复到原始状态。

执行过程如下图所示:

image-20220921113151379

AOF持久化最终需要将缓冲区中的内容写入一个文件,写文件通过操作系统提供的write函数执行。但是write之后数据只是保存在kernel的缓冲区中,真正写入磁盘还需要调用fsync函数。fsync是一个阻塞并且缓慢的操作,所以Redis通appendfsync配置控制执行fsync的频次。具体有如下3种模式:

appendfsync always  ## 每执行一次写入就会执行一次fsync。数据安全性最高但会导致Redis性能降低
appendfsync everysec ## 每1秒执行一次fsync操作。属于折中方案,在数据安全性和性能之间达到一个平衡
appendfsync no ## 不执行fsync,由操作系统负责数据的刷盘。数据安全性最低但Redis性能最高

优缺点

  • AOF丢失的数据更少,刷盘策略为everysec的情况下执行写操作,只会损失一秒钟的写操作数据。
  • AOF日志是一个只能追加的日志,因此在停电时不会出现查找或损坏问题。
  • 当AOF变得太大时,Redis能够在后台自动重写AOF。
  • AOF以一种易于理解和解析的格式,一个接一个地包含所有操作的日志,方便查看和回滚。

缺点:

  • 数据量相同情况下AOF一般比RDB文件大

  • AOF可能比RDB慢,具体取决于具体的fsync策略。通常情况下,设置每秒fsync的性能仍然非常高,禁用fsync的情况下,即使在高负载下,它也应该和RDB一样快。RDB仍然能够提供关于最大延迟的更多保证,即使是在巨大的写负载的情况下。

    注意

    redis<7.0

    • 如果在重写过程中有对数据库的写操作,AOF可能会使用大量内存(这些操作被缓冲在内存中,并在最后写入新的AOF)。
    • 重写期间到达的所有写命令都将写入磁盘两次。

AOF重写

随着Redis服务的运行,AOF文件会越来越大,并且当Redis服务有大量的修改操作时,对同一个键可能有成百上千条执行命令。AOF重写通过fork出一个子进程来执行,重写不会对原有文件进行任何修改和读取,子进程对所有数据库中所有的键各自生成一条相应的执行命令,最后将重写开始后父进程继续执行的命令进行回放,生成一个新的AOF文件。

AOF自动配置如下:

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

当AOF文件大于64MB时,并且AOF文件当前大小比基准大小增长了100%时会触发一次AOF重写。

起始的基准大小为Redis重启并加载完AOF文件之后,aof_buf的大小。当执行完一次AOF重写之后,基准大小相应更新为重写之后AOF文件的大小。

过程如下:

image-20240226163339913

在子进程执行AOF重写期间,服务器进程需要执行以下三个工作:

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

这样一来可以保证:

  • AOF缓冲区的内容会定期被写入和同步到AOF文件,对现有AOF文件的处理工作会如常进行。
  • 从创建子进程开始,服务器执行的所有写命令都会被记录到AOF重写缓冲区里面。当子进程完成AOF重写工作之后,它会向父进程发送一个信号,父进程在接到该信号之后,会调用一个信号处理函数,并执行以下工作:
    • 将AOF重写缓冲区中的所有内容写入到新AOF文件中,这时新AOF文件所保存的数据库状态将和服务器当前的数据库状态一致。
    • 对新的AOF文件进行改名,原子地(atomic)覆盖现有的AOF文件,完成新旧两个AOF文件的替换。这个信号处理函数执行完毕之后,父进程就可以继续像往常一样接受命令请求了。

为什么要将AOF重写放到子进程执行?

Redis不希望AOF重写造成服务器无法处理请求。子进程进行AOF重写期间,服务器进程(父进程)可以继续处理命令请求。子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性。

混合持久化

混合持久化指进行AOF重写时子进程将当前时间点的数据快照保存为RDB文件格式,而后将父进程累积命令保存为AOF格式。格式如下图所示:

image-20220921140014969

加载时,首先会识别AOF文件是否以REDIS字符串开头,如果是,就按RDB格式加载(RDB文件以REDUS字符串开头),加载完RDB后继续按AOF格式加载剩余部分。

开启混合持久化:

aof-use-rdb-preamble yes

参考文章