Redis学习之持久化机制

Catalogue
  1. 1. 前言
  2. 2. RDB
    1. 2.1. 手动触发
    2. 2.2. 自动触发
    3. 2.3. RDB的相关配置
    4. 2.4. 其他机制
  3. 3. AOF
  4. 4. 开启AOF
    1. 4.1. 命令写入
    2. 4.2. 文件同步方式
    3. 4.3. 重写机制
    4. 4.4. 重新加载
  5. 5. 参考资料 & 鸣谢

前言

持久化就是将Redis内存中的数据写入到磁盘中进行存储,因为Redis是内存数据库,数据都是存在内存中的,为了避免进程退出导致数据的丢失,所以需要将数据持久化到硬盘中,这样下次Redis重启后可以利用之前持久化的文件实现数据恢复。

一般有两种持久化方式:

  • 快照 : Redis RDB
  • 写日志: Redis AOF

下面对这两种方式一一进行详细阐述,关于更多Redis持久化深入介绍,可以拜读 Redis持久化的经典之作——Redis persistence demystified

RDB

RDB 持久化是把当前进程数据生成快照保存到硬盘的过程,文件格式是二进制文件,这种利用快照方式保存数据,其实在很多领域都比较常见,比如云服务器备份数据,MySQL备份数据等等。

触发RDB持久化过程一般分为两种方式:

  • 手动触发
  • 自动触发

手动触发

手动触发又分别有两种命令:

  • save命令:阻塞当前的Redis服务器,直到RDB过程完成为止
  • bgsave命令(主流的触发RDB持久化方式): Redis 进程执行 fork操作创建子进程,RDB 持久化由子进程负责,完成后自动结束,阻塞只发生在 fork 阶段

save命令

1
2
127.0.0.1:6379> save
OK

执行 save命令阻塞当前Redis,然后完成时返回一个 OK

bgsave

bgsave的执行流程如下图(出自《Redis开发与运维》):

redis_bgsave

  • 执行 bgsave 命令,Redis 父进程 判断当前是否存在正在执行的子进程,如 RDB/AOF子进程,如果存在 bgsave 命令直接返回
  • 父进程执行 fork 操作创建子进程,fork操作过程中父进程会阻塞,通过 info stats 命令 查看 latest_fork_usec 选项,可以获取最近一个 fork 操作的耗时,单位是 微秒
  • 父进程 fork 完成后,bgsave 命令 返回 Background saving started 信息并不会阻塞父进程,可以继续响应其他命令
  • 子进程创建 RDB 文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换
  • 进程发送信号给父进程表示完成,父进程更新统计信息

自动触发

自动触发机制一般是在配置文件中使用 save m n,表示 m 秒内数据集存在 n 次 修改时,自动触发 bgsave,redis 默认的自动触发机制如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Save the DB on disk:
#
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""

save 900 1
save 300 10
save 60 10000

比如 save 900 1就表示当时间到 900s 时,如果Redis数据至少发生了一次修改,那么就会执行 bgsave

RDB的相关配置

redis.conf有一系列关于 RDB的相关配置,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 快照的文件名
dbfilename dump.rdb

# 存放快照的目录
dir ./

# 在进行快照备份时,是否进行压缩。
# yes:压缩,但是需要一些cpu的消耗。
# no:不压缩,需要更多的磁盘空间。
rdbcompression yes

# 是否开启RDB文件的校验,在写入文件和读取文件时都起作用,关闭时,写入和启动文件有大约10%的性能提升,但无法检查RDB的损坏情况
rdbchecksum yes

#自动触发
#900秒后且至少1个key发生变化时创建快照
save 900 1
#300秒后且至少10个key发生变化时创建快照
save 300 10
#60秒后且至少10000个key发生变化时创建快照
save 60 10000

一旦数据库出现问题,那么我们的RDB文件中保存的数据并不是全新的,从上次RDB文件生成到Redis停机这段时间的数据全部丢掉了。例如,每隔5分钟或者更长的时间来创建一次快照,Redis停止工作时(例如意外断电)就可能丢失最近几分钟的数据。所以RDB 不适合实时持久化操作

其他机制

  • 全量复制: 如果从节点执行全量复制,主节点自动执行 bgsave 命令 生成 RDB 文件并发送给从节点
  • debug reload: 重新加载redis会触发 save操作
  • shutdown命令: 如果没有开启 AOF 持久化功能则自动执行 bgsave

AOF

AOF (append only file) 持久化: 以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令以达到恢复数据的目的,类似于 MySQL binlog。

与 RDB 相比,AOF 的实时性更好。

开启AOF

redis.conf配置文件中开启 AOF持久化

1
2
3
4
5
6
7
8
9
10
# 是否开启AOF,默认关闭(no)
appendonly yes

# AOF 文件名 (默认: "appendonly.aof")

appendfilename "appendonly.aof"

# 保存路径与 RDB 一样,可配置

dir ./

使用AOF流程:

  • 命令写入(append): 所有的写入命令会追加到 aof_buf 缓冲区中
  • 文件同步(sync): AOF 缓冲区根据对应的策略向硬盘做同步操作(sync)
  • 文件重写(rewrite): 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的
  • 重启加载(load): 当Redis重启时,加载AOF文件进行数据恢复

命令写入

Redis先把命令追加到 aof_buf缓冲区中,该缓冲区是在系统的用户态缓冲区中,更具体的讲是在 Redis的缓冲内存中,缓冲内存主要包括: 客户端缓冲,复制积压缓冲区,AOF缓冲区。所以这里Redis实际上是将命令追加到AOF缓冲区。

之所以先写入缓冲区,是为了避免每次写 AOF 文件命令都直接写入硬盘,导致硬盘IO成为Redis负载的瓶颈。

AOF 命令写入的内容直接是文本协议格式,一种Redis制定的文本协议,详情请查阅相关资料。它是一种纯文本格式,具有很好的兼容性,开启AOF后,所有写入命令都包含追加操作,直接采用 协议格式,避免了二次处理开销。

文件同步方式

文件同步本质上将在用户态缓冲区(具体说是AOF缓冲区)中的数据 通过System call 刷入到操作系统的内核态中,进入刷入硬盘中去。

其实涉及的两个重要的系统调用(System Call):

  • write: 通过 write 系统调用,将数据写入到内核缓冲区中后直接返回,后面的工作交由操作系统将内核缓冲区的数据写入硬盘,这样的好处是提高了IO性能
  • fsync:通过 fsync 系统调用 将数据提交到硬盘中,强制硬盘同步,将一直阻塞到写入硬盘完成后返回

而文件同步又有三种策略,可在 redis.conf配置文件中进行配置:

  • appendfsync always: 单纯靠调用 fsync将数据刷入到硬盘,是最有保证的完全的持久化,但速度也是最慢的,因为fsync存在阻塞,一般不推荐使用。
  • appendfsync everysec: 每隔一秒钟,数据会通过write写入到内核缓冲完成后返回。为了进一步保证持久化,又专门后台开一个线程通过fsync将数据刷新到磁盘,以避免内核缓冲内数据因系统故障宕机丢失。在性能和持久化方面做了很好的折中,是受推荐的方式。
  • appendfsync no: 通过write系统调用将数据写入到内核缓存,然后依赖OS的写入,将数据从内核缓存写入到硬盘,一般为30秒左右一次。性能最好但是持久化最没有保证,不被推荐,如果有系统宕机故障,最近30秒的数据有可能丢失

重写机制

随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制压缩文件体积。 经过重写,比如多条命令合并成一条命令等操作,然后重写后的AOF文件可以变小,以便更快被Redis加载

AOF重写过程分为手动触发和自动触发:

  • 手动触发:直接调用 bgrewriteaof命令
  • 自动触发: 两大参数
    • auto-aof-rewrite-min-size:表示运行 AOF重写时文件最小体积,默认为64MB
    • auto-aof-rewrite-percentage : AOF增长率,当前AOF文件空间和上一次重写后AOF文件空间的比值

redis.conf配置文件中,自动触发参数设置如下:

1
2
3
#当AOF增长率为100%且达到了64mb时开始自动重写AOF  
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

重新加载

Redis 持久化文件加载流程如下,如果 RDB 与 AOF同时开启,优先使用 AOF (下图摘自《Redis开发与运维》):

redis_aof_reload

参考资料 & 鸣谢

Bagikan Komentar