Redis持久化

Redis 的读写都是在内存中,所以它的性能较高,但在内存中的数据会随着服务器的重启而丢失,为了保证数据不丢失,我们需要将内存中的数据存储到磁盘,以便 Redis 重启时能够从磁盘中恢复原有的数据,而整个过程就叫做 Redis 持久化。

:下文中所有直接通过命令开启的配置都无需重启 Redis 服务,但是 Redis 服务重启,则之前使用命令行设置的配置就会失效。

持久化的几种方式

Redis 持久化拥有以下三种方式:

  • 快照方式(RDB, Redis DataBase)将某一个时刻的内存数据,以二进制的方式写入磁盘;
  • 文件追加方式(AOF, Append Only File),记录所有的操作命令,并以文本的形式追加到文件中;
  • 混合持久化方式,Redis 4.0 之后新增的方式,混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,这样既能保证 Redis 重启时的速度,又能减低数据丢失的风险。

RDB(Redis DataBase)

介绍

       在指定的时间间隔内生成内存中整个数据集的持久化快照。快照文件以二进制的形式被存储在当前文件夹中,默认名称为dump.rdb,可以通过dir和dbfilename参数来修改默认值。

        Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。  整个过程中,主进程是不进行任何的IO操作的,这就确保了极高的性能。

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# redis是基于内存的数据库,可以通过设置该值定期写入磁盘。
# 注释掉“save”这一行配置项就可以让保存数据库功能失效
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 900 1
save 300 10
save 60 10000

#当RDB持久化出现错误后,是否依然进行继续进行工作,yes:不能进行工作,no:可以继续进行工作,可以通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误
stop-writes-on-bgsave-error yes

#使用压缩rdb文件,rdb文件压缩使用LZF压缩算法,yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间
rdbcompression yes

#表示写入文件和读取文件时是否开启 RDB 文件检查,检查是否有无损坏,默认为yes,如果在启动时检查发现损坏,则停市启动。
#从rdb格式的第五个版本开始,在rdb文件的末尾会带上CRC64的校验和。这跟有利于文件的容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗,所以如果你追求高性能,可以关闭该配置。
rdbchecksum yes

#rdb文件的名称
dbfilename dump.rdb

#数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
dir /data

Fork

      fork的作用相当于复制一个与当前进程一样的进程。但是是一个全新的进程,并作为原进程的子进程。

持久化触发条件

RDB 的持久化触发方式有两类:一类是手动触发,另一类是自动触发。

手动触发

手动触发分为手动执行save和bgsave两种,主要区别是后者不会阻碍主线程的执行

  • save命令

    在客户端中执行 save 命令,就会触发 Redis 的持久化,但同时也是使 Redis 处于阻塞状态,直到 RDB 持久化完成,才会响应其他客户端发来的命令,所以在生产环境一定要慎用

  • bgsave 命令

    bgsave(background save)既后台保存的意思, 它和 save 命令最大的区别就是 bgsave 会 fork() 一个子进程来执行持久化,整个过程中只有在 fork() 子进程时有短暂的阻塞,当子进程被创建之后,Redis 的主进程就可以响应其他客户端的请求了,相对于整个流程都阻塞的 save 命令来说,显然 bgsave 命令更适合我们使用。

自动触发

  1. save m n (配置)

    表示在m秒内,有n个键发生改变,则自动触发持久化

    1
    2
    3
    4
    # 详细说明见上文配置文件处
    save 900 1
    save 300 10
    save 60 10000
  2. flushall命令

    flushall 命令用于清空 Redis 数据库,在生产环境下一定慎用,当 Redis 执行了 flushall 命令之后,则会触发自动持久化,把 RDB 文件清空

  3. shutdown命令

    注:安全退出,也会生成快照文件(和异常退出形成对比,比如:kill杀死进程的方式)

  4. 主从同步触发

    在 Redis 主从复制中,当从节点执行全量复制操作时,主节点会执行 bgsave 命令,并将 RDB 文件发送给从节点,该过程会自动触发 Redis 持久化。

RDB文件如何恢复

当 Redis 服务器启动时,如果 Redis 配置中的dir目录存在 RDB 文件 dump.rdb,Redis 就会自动加载 RDB 文件恢复持久化数据。 如果 配置中的dir目录没有 dump.rdb 文件,请先将 dump.rdb 文件移动到 Redis 的根目录。

RDB持久化禁用

禁用持久化可以提高 Redis 的执行效率,如果对数据丢失不敏感的情况下,可以注释save相关的配置或在连接客户端的情况下,执行 config set save "" 命令即可禁用 Redis 的持久化。

RDB优势

  1. 恢复数据的速度很快,适合大规模的数据恢复,而又对部分数据不敏感的情况
  2. dump.db文件是一个压缩的二进制文件,文件暂用空间小
  3. RDBRedis的性能影响非常小,是因为在同步数据的时候他只是fork了一个子进程去做持久化的,而且他在数据恢复的时候速度比AOF来的快。
  4. 与 AOF 格式的文件相比,RDB 文件可以更快的重启。

RDB劣势

  1. RDB都是快照文件,都是默认五分钟甚至更久的时间才会生成一次,这意味着你这次同步到下次同步这中间五分钟的数据都很可能全部丢失掉。AOF则最多丢一秒的数据,数据完整性上高下立判。
  2. 当fork的时候,内存的中的数据会被克隆一份,大致两倍的膨胀需要考虑。而且,当数据过大时,fork操作占用过多的系统资源,比如你公司在做秒杀的时候他刚好在这个时候fork了一个子进程去生成一个大快照,造成主服务器进程假死几率就很高了。

使用场景

  1. 数据备份
  2. 可容忍部分数据丢失
  3. 跨数据中心的容灾备份

AOF(Append Only File)

使用 RDB 持久化有一个风险,它可能会造成最新数据丢失的风险。因为 RDB 的持久化有一定的时间间隔,在这个时间段内如果 Redis 服务意外终止的话,就会造成最新的数据全部丢失。

可能会操作 Redis 服务意外终止的条件:

  • 安装 Redis 的机器停止运行,蓝屏或者系统崩溃;
  • 安装 Redis 的机器出现电源故障,例如突然断电;
  • 使用 kill -9 Redis_PID 等。

那么如何与之匹配的解决方案就是使用AOF。

介绍

以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作补不可记录),只许追加文件但不可以改写文件,redis启动之初会读取改文件重新构建数据,保存的是appendonly.aof文件

aof持久化的一些策略配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 开启aof机制,默认为no
appendonly yes

# 指定aof文件名称
appendfilename myaoffile.aof

#aof持久化策略的配置
#no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
#always表示每次写入都执行fsync,以保证数据同步到磁盘。
#everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。
appendfsync everysec

#数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
dir /data

# 是否开启启动时加载 AOF 文件效验,默认值是 yes,表示尽可能的加载 AOF 文件,忽略错误部分信息,并启动 Redis 服务。如果值为 no,则表示,停止启动 Redis,用户必须手动修复 AOF 文件才能正常启动 Redis 服务。
aof-load-truncated yes

触发条件

同样的,在AOF 持久化开启之后,只要满足一定条件,就会触发 AOF 持久化。AOF 的触发条件分为两种:自动触发和手动触发。

自动触发

有两种情况可以自动触发 AOF 持久化,分为是:满足 AOF 设置的策略触发满足 AOF 重写触发。其中,AOF 重写触发会在本文的后半部分详细介绍,这里重点来说 AOF 持久化策略都有哪些。 AOF 持久化策略,分为以下三种:

  • always:每条 Redis 操作命令都会写入磁盘,最多丢失一条数据;
  • everysec:每秒钟写入一次磁盘,最多丢失一秒的数据;
  • no:不设置写入磁盘的规则,根据当前操作系统来决定何时写入磁盘,Linux 默认 30s 写入一次数据至磁盘。
手动触发

在客户端执行 bgrewriteaof 命令就可以手动触发 AOF 持久化

AOF 文件重写

AOF 是通过记录 Redis 的执行命令来持久化(保存)数据的,所以随着时间的流逝 AOF 文件会越来越多,这样不仅增加了服务器的存储压力,也会造成 Redis 重启速度变慢,为了解决这个问题 Redis 提供了 AOF 重写的功能。

为什么AOF需要重写

AOF 重写指的是它会直接读取 Redis 服务器当前的状态,并压缩保存为 AOF 文件。例如,我们增加了一个计数器,并对它做了 99 次修改,如果不做 AOF 重写的话,那么持久化文件中就会有 100 条记录执行命令的信息,而 AOF 重写之后,之后记录一条此计数器最终的结果信息,这样就去除了所有的无效信息

AOF 重写实现

对于触发aof重写机制也可以通过配置文件来进行设置:

1
2
3
4
# aof自动重写配置。当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
auto-aof-rewrite-percentage 100
# 设置允许重写的最小aof文件大小,默认是 64mb,避免了达到约定百分比但尺寸仍然很小的情况还要重写
auto-aof-rewrite-min-size 64mb

当aop重写时会引发重写和持久化追加同时发生的问题,可以通过no-appendfsync-on-rewrite no进行配置

1
2
# 在aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysec和always的aof模式来说,执行fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题。如果对延迟要求很高的应用,这个字段可以设置为yes,,设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,不会造成阻塞的问题(因为没有磁盘竞争),等rewrite完成后再写入,这个时候redis会丢失数据。Linux的默认fsync策略是30秒。可能丢失30秒数据。因此,如果应用系统无法忍受延迟,而可以容忍少量的数据丢失,则设置为yes。如果应用系统无法忍受数据丢失,则设置为no。
no-appendfsync-on-rewrite no

AOF重写流程

AOF 文件重写是生成一个全新的文件,并把当前数据的最少操作命令保存到新文件上,当把所有的数据都保存至新文件之后,Redis 会交换两个文件,并把最新的持久化操作命令追加到新文件上。

AOF如何进行数据恢复

正常恢复流程

       只要开启了 AOF 持久化,并将文件放到dir指定的文件夹下,当redis启动的时候会自动加载数据,注意:aof文件的优先级比dump大

持久化文件加载规则
  • 如果只开启了 AOF 持久化,Redis 启动时只会加载 AOF 文件(appendonly.aof),进行数据恢复;
  • 如果只开启了 RDB 持久化,Redis 启动时只会加载 RDB 文件(dump.rdb),进行数据恢复;
  • 如果同时开启了 RDB 和 AOF 持久化,Redis 启动时只会加载 AOF 文件(appendonly.aof),进行数据恢复。

在 AOF 开启的情况下,即使 AOF 文件不存在,只有 RDB 文件,也不会加载 RDB 文件。 AOF 和 RDB 的加载流程如下图所示:

image.png

异常恢复流程

在 AOF 写入文件时如果服务器崩溃,或者是 AOF 存储已满的情况下,AOF 的最后一条命令可能被截断,这就是异常的 AOF 文件。

  • 简单异常

    在 AOF 文件异常的情况下,如果为修改 Redis 的配置文件,也就是使用 aof-load-truncated 等于 yes 的配置,Redis 在启动时会忽略最后一条命令,并顺利启动 Redis,执行结果如下:

1
2
3
4
5
* Reading RDB preamble from AOF file...
* Reading the remaining AOF tail...
# !!! Warning: short read while loading the AOF file !!!
# !!! Truncating the AOF at offset 439 !!!
# AOF loaded anyway because aof-load-truncated is enabled
  • 复杂异常数据

AOF 文件可能出现更糟糕的情况,当 AOF 文件不仅被截断,而且中间的命令也被破坏,这个时候再启动 Redis 会提示错误信息并中止运行。

解决方案如下:

  1. 首先使用 AOF 修复工具,检测出现的问题,在命令行中输入 redis-check-aof 命令,它会跳转到出现问题的命令行,这个时候可以尝试手动修复此文件;

    eg:使用了flushall这个命令,此刻持久化文件中就会有这么一条命令记录,把它删掉就可以了

  2. 如果无法手动修复,我们可以使用 redis-check-aof --fix 自动修复 AOF 异常文件,不过执行此命令,可能会导致异常部分至文件末尾的数据全部被丢弃。

AOF优势

  1. 根据不同的策略,可以实现每秒,每一次修改操作的同步持久化,就算在最恶劣的情况下只会丢失不会超过两秒数据。
  2. AOF在对日志文件进行操作的时候是以append-only的方式去写的,他只是追加的方式写数据,自然就少了很多磁盘寻址的开销了,写入性能惊人,文件也不容易破损。
  3. 当文件太大时,会触发重写机制,确保文件不会太大。
  4. 文件可以简单的读懂,比如通过flushall误操作清空了所有的数据,只要这个时候后台重写还没发生,马上拷贝一份AOF日志文件,把最后一条flushall命令删了就完事了。

AOF劣势

  1. AOF文件的大小太大,就算有重写机制,但重写所造成的阻塞问题是不可避免的
  2. AOF开启后,Redis支持写的QPS会比RDB支持写的要低,最坏的情况是每秒都要去异步刷新一次日志fsync,当然即使这样性能还是很高。

混合持久化

RDB 和 AOF 持久化各有利弊,RDB 可能会导致一定时间内的数据丢失,而 AOF 由于文件较大则会影响 Redis 的启动速度,为了能同时使用 RDB 和 AOF 各种的优点,Redis 4.0 之后新增了混合持久化的方式。

在开启混合持久化的情况下,AOF 重写时会把 Redis 的持久化数据,以 RDB 的格式写入到 AOF 文件的开头,之后的数据再以 AOF 的格式化追加的文件的末尾。

混合持久化的数据存储结构如下图所示:

image.png

混合持久化开启

混合持久化开启只需要将配置文件中的 aof-use-rdb-preamble no 改为 aof-use-rdb-preamble yes 即可,也可使用命令 config set aof-use-rdb-preamble yes开启。

实测

当在混合持久化关闭的情况下,使用 bgrewriteaof 触发 AOF 文件重写之后,查看 appendonly.aof 文件的持久化日志,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
127.0.0.1:6379>  config set aof-use-rdb-preamble no
OK
127.0.0.1:6379> config get aof-use-rdb-preamble
1) "aof-use-rdb-preamble"
2) "no"
127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
127.0.0.1:6379> exit
/bin # cat ../data/appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
SET
$2
k1
$7
.....

可以看出,当混合持久化关闭的情况下 AOF 持久化文件存储的为标准的 AOF 格式的文件。

当混合持久化开启的模式下,使用 bgrewriteaof 命令触发 AOF 文件重写,得到 appendonly.aof 的文件内容如下图所示:

image-20221208091135073

可以看出 appendonly.aof 文件存储的内容是 REDIS 开头的 RDB 格式的内容,并非为 AOF 格式的日志。

Tips:AOF 格式的开头是 *,而 RDB 格式的开头是 REDIS。

混合持久化流程

混合持久化的加载流程如下:

  1. 判断是否开启 AOF 持久化,开启继续执行后续流程,未开启执行加载 RDB 文件的流程;
  2. 判断 appendonly.aof 文件是否存在,文件存在则执行后续流程;
  3. 判断 AOF 文件开头是 RDB 的格式, 先加载 RDB 内容再加载剩余的 AOF 内容;
  4. 判断 AOF 文件开头不是 RDB 的格式,直接以 AOF 格式加载整个文件。

![image.png](https://learn.lianglianglee.com/专栏/Redis 核心原理与实战/assets/2020-02-24-122601.png)

混合持久化优缺点

混合持久化优点:
  • 混合持久化结合了 RDB 和 AOF 持久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF 的优点,有减低了大量数据丢失的风险。
混合持久化缺点:
  • AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件的可读性变得很差;
  • 兼容性差,如果开启混合持久化,那么此混合持久化 AOF 文件,就不能用在 Redis 4.0 之前版本了。

最佳实践

持久化虽然保证了数据不丢失,但同时拖慢了 Redis 的运行速度,那怎么更合理的使用 Redis 的持久化功能呢? Redis 持久化可从以下几个方面考虑。

1) 控制持久化开关

使用者可根据实际的业务情况考虑,如果对数据的丢失不敏感的情况下,可考虑关闭 Redis 的持久化,这样所以的键值操作都在内存中,就可以保证最高效率的运行 Redis 了。 持久化关闭操作:

  • 关闭 RDB 持久化,使用命令: config set save ""
  • 关闭 AOF 和 混合持久化,使用命令: config set appendonly no

2) 主从部署

使用主从部署,一台用于响应主业务,一台用于数据持久化,这样就可能让 Redis 更加高效的运行。

3) 使用混合持久化

混合持久化结合了 RDB 和 AOF 的优点,Redis 5.0 默认是开启的。

4) 使用配置更高的机器

Redis 对 CPU 的要求并不高,反而是对内存和磁盘的要求很高,因为 Redis 大部分时候都在做读写操作,使用更多的内存和更快的磁盘,对 Redis 性能的提高非常有帮助。

总结

  1. 如果你只希望你的数据在服务器运行的时候存在,可以不使用任何的持久化方式

  2. 一般建议同时开启两种持久化方式。AOF进行数据的持久化,确保数据不会丢失太多,而RDB更适合用于备份数据库,留着一个做万一的手段。

  3. 性能建议:

    因为RDB文件只用做后备用途,建议只在slave上持久化RDB文件,而且只要在15分钟备份一次就够了,只保留900 1这条规则。

    如果Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。

    代价:

    1、带来了持续的IO;

    2、AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。

    如果不Enable AOF,仅靠Master-Slave Replication 实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时宕掉,会丢失10几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。新浪微博就选用了这种架构。