Redis的持久化机制

Redis是基于内存的非关系型K-V数据库,既然它是基于内存的,如果Redis服务器挂了,数据就会丢失。为了避免数据丢失了,Redis提供了持久化,即把数据保存到磁盘。

Redis提供了RDB和AOF两种持久化机制,它持久化文件加载流程如下:

一、RDB(Redis DataBase)

RDB,就是把内存数据以快照的形式保存到磁盘上。

什么是快照?可以这样理解,给当前时刻的数据,拍一张照片,然后保存下来。

RDB持久化,是指在指定的时间间隔内,执行指定次数的写操作,将内存中的数据集快照写入磁盘中,它是Redis默认的持久化方式。执行完操作后,在指定目录下会生成一个dump.rdb文件,Redis 重启的时候,通过加载dump.rdb文件来恢复数据。RDB触发机制主要有以下几种:

1.1 SAVE:阻塞服务器并创建RDB文件

用户可以通过执行SAVE命令,要求Redis服务器以同步方式创建出一个记录了服务器当前所有数据库数据的RDB文件。SAVE命令是一个无参数命令,它在创建RDB文件成功时将返回OK作为结果。

bash 复制代码
SAVE

接收到SAVE命令的Redis服务器将遍历数据库包含的所有数据库,并将各个数据库包含的键值对全部记录到RDB文件中。在SAVE命令执行期间,Redis服务器将阻塞,直到RDB文件创建完毕为止。如果Redis服务器在执行SAVE命令时已经拥有了相应的RDB文件,那么服务器将使用新创建的RDB文件代替已有的RDB文件。

1.2 BGSAVE:以非阻塞方式创建RDB文件

因为SAVE命令在执行时会阻塞整个服务器,所以用户在使用该命令创建RDB文件期间将无法为其他客户端提供服务。为了解决这个问题,Redis提供了SAVE命令的异步版本BGSAVE命令:这个命令与SAVE命令一样都是无参数命令,它与SAVE命令的不同之处在于,BGSAVE不会直接使用Redis服务器进程创建RDB文件,而是使用子进程创建RDB文件。

bash 复制代码
BGSAVE

当Redis服务器接收到用户发送的BGSAVE命令时,将执行以下操作:

1)创建一个子进程。

2)子进程执行SAVE命令,创建新的RDB文件。

3)RDB文件创建完毕之后,子进程退出并通知Redis服务器进程(父进程)新RDB文件已经完成。

4)Redis服务器进程使用新RDB文件替换已有的RDB文件。

因为BGSAVE命令是以异步方式执行的,所以Redis服务器在BGSAVE命令执行期间仍然可以继续处理其他客户端发送的命令请求。不过需要注意的是,虽然BGSAVE命令不会像SAVE命令那样一直阻塞Redis服务器,但由于执行BGSAVE命令需要创建子进程,所以父进程占用的内存数量越大,创建子进程这一操作耗费的时间也会越长,因此Redis服务器在执行BGSAVE命令时,仍然可能会由于创建子进程而被短暂地阻塞。

1.3 通过配置选项自动创建RDB文件

用户除了可以使用SAVE命令和BGSAVE命令手动创建RDB文件之外,还可以通过设置save选项,让Redis服务器在满足指定条件时自动执行BGSAVE命令:

bash 复制代码
save <seconds> <changes>

save选项接受seconds和changes两个参数,前者用于指定触发持久化操作所需的时长,而后者则用于指定触发持久化操作所需的修改次数。简单来说,如果服务器在seconds秒之内,对其包含的各个数据库总共执行了至少changes次修改,那么服务器将自动执行一次BGSAVE命令。

  1. Redis允许用户同时向服务器提供多个save选项,当给定选项中的任意一个条件被满足时,服务器就会执行一次BGSAVE。
bash 复制代码
        save 6000 1
        save 600100
        save 6010000
  1. RDB持久化是Redis默认使用的持久化方式,如果用户在启动Redis服务器时,既没有显式地关闭RDB持久化功能,也没有启用AOF持久化功能,那么Redis默认将使用以下save选项进行RDB持久化:
bash 复制代码
        save 6010000
        save 300100
        save 3600 1

1.4 SAVE命令和BGSAVE命令的选择

因为SAVE命令在创建RDB文件期间会阻塞Redis服务器,所以如果我们需要在创建RDB文件的同时让Redis服务器继续为其他客户端服务,那么就只能使用BGSAVE命令来创建RDB文件。

因为SAVE命令无须创建子进程,它不会因为创建子进程而消耗额外的内存,所以在维护离线的Redis服务器时,使用SAVE命令能够比使用BGSAVE命令更快地完成创建RDB文件的工作。

1.5 RDB文件结构

1.6 载入RDB文件

首先,当Redis服务器启动时,它会在工作目录中查找是否有RDB文件出现,如果有就打开它,然后读取文件的内容并执行以下载入操作:

1.7 数据丢失

RDB文件记录的是服务器在开始创建文件的那一刻,服务器中包含的所有键值对数据,这种数据持久化方式通常被称为时间点快照(point-in-time snapshot)。时间点快照持久化的一个特点是,系统在停机时将丢失最后一次成功实施持久化之后的所有数据。对于一个只使用RDB持久化的Redis服务器来说,服务器停机时丢失的数据量将取决于最后一次成功执行的RDB持久化操作,以及该操作开始执行的时间。

SAVE命令的停机情况:

因为SAVE命令是一个同步操作,它的开始和结束都位于同一个原子时间之内,所以如果用户使用SAVE命令进行持久化,那么服务器在停机时将丢失最后一次成功执行SAVE命令之后产生的所有数据。

BGSAVE命令的停机情况:

因为BGSAVE命令是一个异步命令,它的开始和结束并不位于同一个原子时间之内,所以如果用户使用BGSAVE命令进行持久化,那么服务器在停机时丢失的数据量将取决于最后一次成功执行的BGSAVE命令的开始时间。

1.8 RDB持久化的缺陷

无论用户使用的是SAVE命令还是BGSAVE命令,停机时服务器丢失的数据量将取决于创建RDB文件的时间间隔:间隔越长,停机时丢失的数据也就越多。

然而矛盾之处在于,RDB持久化是一种全量持久化操作,它在创建RDB文件时需要存储整个服务器包含的所有数据,并因此消耗大量计算资源和内存资源,所以用户是不太可能通过增大RDB文件的生成频率来保证数据安全的。

从RDB持久化的特征来看,它更像是一种数据备份手段而非一种普通的数据持久化手段。为了解决RDB持久化在停机时可能会丢失大量数据这一问题,并提供一种真正符合用户预期的持久化功能,Redis推出了15.2节将要介绍的AOF持久化模式。

二、AOF(append only file)

与全量式的RDB持久化功能不同,AOF提供的是增量式的持久化功能,这种持久化的核心原理在于:服务器每次执行完写命令之后,都会以协议文本的方式将被执行的命令追加到AOF文件的末尾。这样一来,服务器在停机之后,只要重新执行AOF文件中保存的Redis命令,就可以将数据库恢复至停机之前的状态。它主要解决数据持久化的实时性问题。默认是不开启的。将redis.conf配置文件中appendonly设置为yes,启动AOF,然后把有数据的appendonly.aof文件复制一份保存到对应目录,重启redis时会重新加载appendonly.aof文件,达到恢复数据的效果。

2.1 打开AOF持久化功能

用户可以通过服务器的appendonly选项来决定是否打开AOF持久化功能:

bash 复制代码
        appendonly <value>
//如果用户想要开启AOF持久化功能,那么只需要将这个值设置为yes即可:
        appendonly yes
//反之,如果用户想要关闭AOF持久化功能,那么只需要将这个值设置为no即可:
        appendonly no

当AOF持久化功能处于打开状态时,Redis服务器在默认情况下将创建一个名为appendonly. aof的文件作为AOF文件。

2.2 设置AOF文件的冲洗频率

为了提高程序的写入性能,现代化的操作系统通常会把针对硬盘的多次写操作优化为一次写操作。具体的做法是,当程序调用write系统调用对文件进行写入时,系统并不会直接把数据写入硬盘,而是会先将数据写入位于内存的缓冲区中,等到指定的时限到达或者满足某些写入条件时,系统才会执行flush系统调用,将缓冲区中的数据冲洗至硬盘。

这种优化机制虽然提高了程序的性能,但是也给程序的写入操作带来了不确定性,特别是对于AOF这样的持久化功能来说,AOF文件的冲洗机制将直接影响AOF持久化的安全性。为了消除上述机制带来的不确定性,Redis向用户提供了appendfsync选项,以此来控制系统冲洗AOF文件的频率:

bash 复制代码
        appendfsync <value>

appendfsync选项拥有always、everysec和no 3个值可选,它们代表的意义分别为:

always------每执行一个写命令,就对AOF文件执行一次冲洗操作。

everysec------每隔1s,就对AOF文件执行一次冲洗操作。

no------不主动对AOF文件执行冲洗操作,由操作系统决定何时对AOF进行冲洗。

这3种不同的冲洗策略不仅会直接影响服务器在停机时丢失的数据量,还会影响服务器在运行时的性能:

在使用always值的情况下,服务器在停机时最多只会丢失一个命令的数据,但使用这种冲洗方式将使Redis服务器的性能降低至传统关系数据库的水平。

在使用everysec值的情况下,服务器在停机时最多只会丢失1s之内产生的命令数据,这是一种兼顾性能和安全性的折中方案。

在使用no值的情况下,服务器在停机时将丢失系统最后一次冲洗AOF文件之后产生的所有命令数据,至于数据量的具体大小则取决于系统冲洗AOF文件的频率。

因为no策略给可能丢失的数据量带来了不确定性,而always策略对于安全性的追求又牺牲了服务器的性能,所以Redis使用everysec作为appendfsync选项的默认值。除非有明确的需求,否则用户不应该随意修改appendfsync选项的值。

2.3 AOF重写

随着服务器不断运行,被执行的命令将变得越来越多,而负责记录这些命令的AOF文件也会变得越来越大。与此同时,如果服务器曾经对相同的键执行过多次修改操作,那么AOF文件中还会出现多个冗余命令。

为了减少冗余命令,让AOF文件保持"苗条",并提供数据恢复操作的执行速度,Redis提供了AOF重写功能,该功能能够生成一个全新的AOF文件,并且文件中只包含恢复当前数据库所需的尽可能少的命令。

重写前:

bash 复制代码
        SELECT 0
        SET msg "hello world! "
        SET msg "good morning! "
        SET msg "happy birthday! "
        SADD fruits "apple"
        SADD fruits "banana"
        SADD fruits "cherry"
        SADD fruits "dragon fruit"
        SREM fruits "dragon fruit"
        SADD fruits "durian"
        RPUSH job-queue 10086
        RPUSH job-queue 12345
        RPUSH job-queue 256512

重写后:

bash 复制代码
        SELECT 0
        SET msg "happy birthday! "
        SADD fruits "apple" "banana" "cherry" "durian"
        RPUSH job-queue 1008612345256512
2.3.1 BGREWRITEAOF命令

用户可以通过执行BGREWRITEAOF命令显式地触发AOF重写操作,该命令是一个无参数命令:

 BGREWRITEAOF

BGREWRITEAOF命令是一个异步命令,Redis服务器在接收到该命令之后会创建出一个子进程,由它扫描整个数据库并生成新的AOF文件。当新的AOF文件生成完毕,子进程就会退出并通知Redis服务器(父进程),然后Redis服务器就会使用新的AOF文件代替已有的AOF文件,借此完成整个重写操作。

关于BGREWRITEAOF还有两点需要注意:首先,如果用户发送BGREWRITEAOF命令请求时,服务器正在创建RDB文件,那么服务器将把AOF重写操作延后到RDB文件创建完毕之后再执行,从而避免两个写硬盘操作同时执行导致机器性能下降;其次,如果服务器在执行重写操作的过程中,又接收到了新的BGREWRITEAOF命令请求,那么服务器将返回错误。

2.3.2 AOF重写配置选项

用户除了可以手动执行BGREWRITEAOF命令创建新的AOF文件之外,还可以通过设置以下两个配置选项让Redis自动触发BGREWRITEAOF命令:

bash 复制代码
//其中auto-aof-rewrite-min-size选项用于设置触发自动AOF文件重写所需的最小AOF文件体积        
auto-aof-rewrite-min-size <value>
//auto-aof-rewrite-percentage,它控制的是触发自动AOF文件重写所需的文件体积增大比例
auto-aof-rewrite-percentage <value>
auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-percentage 100

2.4 AOF持久化的优缺点

但是与RDB持久化相比,AOF持久化也有相应的缺点:

首先,因为AOF文件存储的是协议文本,所以它的体积会比包含相同数据、二进制格式的RDB文件要大得多,并且生成AOF文件所需的时间也会比生成RDB文件所需的时间更长。

其次,因为RDB持久化可以直接通过RDB文件恢复数据库数据,而AOF持久化则需要通过执行AOF文件中保存的命令来恢复数据库(前者是直接的数据恢复操作,而后者则是间接的数据恢复操作),所以RDB持久化的数据恢复速度将比AOF持久化的数据恢复速度快得多,并且数据库体积越大,这两者之间的差距就会越明显。

最后,因为AOF重写使用的BGREWRITEAOF命令与RDB持久化使用的BGSAVE命令一样都需要创建子进程,所以在数据库体积较大的情况下,进行AOF文件重写将占用大量资源,并导致服务器被短暂地阻塞。

三、 RDB-AOF混合持久化

Redis从4.0版本开始引入RDB-AOF混合持久化模式,这种模式是基于AOF持久化模式构建而来的------如果用户打开了服务器的AOF持久化功能,并且将选项的值设置成了yes,那么Redis服务器在执行AOF重写操作时,就会像执行BGSAVE命令那样,根据数据库当前的状态生成出相应的RDB数据,并将这些数据写入新建的AOF文件中,至于那些在AOF重写开始之后执行的Redis命令,则会继续以协议文本的方式追加到新AOF文件的末尾,即已有的RDB数据的后面。换句话说,在开启了RDB-AOF混合持久化功能之后,服务器生成的AOF文件将由两个部分组成,其中位于AOF文件开头的是RDB格式的数据,而跟在RDB数据后面的则是AOF格式的数据。

bash 复制代码
        aof-use-rdb-preamble <value>

通过使用RDB-AOF混合持久化功能,用户可以同时获得RDB持久化和AOF持久化的优点:服务器既可以通过AOF文件包含的RDB数据来实现快速的数据恢复操作,又可以通过AOF文件包含的AOF数据来将丢失数据的时间窗口限制在1s之内。Redis目前默认是没有打开RDB-AOF混合持久化功能的。

相关推荐
Rookie也要加油10 分钟前
01_SQLite
数据库·sqlite
liuxin3344556614 分钟前
教育技术革新:SpringBoot在线教育系统开发
数据库·spring boot·后端
看山还是山,看水还是。1 小时前
MySQL 管理
数据库·笔记·mysql·adb
fishmemory7sec1 小时前
Koa2项目实战2(路由管理、项目结构优化)
数据库·mongodb·koa
momo小菜pa1 小时前
【MySQL 09】表的内外连接
数据库·mysql
Jasonakeke1 小时前
【重学 MySQL】四十九、阿里 MySQL 命名规范及 MySQL8 DDL 的原子化
数据库·mysql
程序猿小D1 小时前
第二百六十九节 JPA教程 - JPA查询OrderBy两个属性示例
java·开发语言·数据库·windows·jpa
小宇成长录2 小时前
Mysql:数据库和表增删查改基本语句
数据库·mysql·数据库备份
团儿.2 小时前
解锁MySQL高可用新境界:深入探索MHA架构的无限魅力与实战部署
数据库·mysql·架构·mysql之mha架构
程序猿小D3 小时前
第二百六十七节 JPA教程 - JPA查询AND条件示例
java·开发语言·前端·数据库·windows·python·jpa