谈谈 Redis 持久化

什么是 Redis 持久化

Redis 是什么这里就不多介绍了,作为一款 NoSQL 数据库来说,Redis 的相关的所有数据操作都是基于内存的,如下图:
谈谈 Redis 持久化_第1张图片
基于内存有一个最大的问题就是,由于各种原因造成服务器的重启,那么内存中的数据就会消失。为了要解决这个问题,Redis 提供了对持久化的支持,我们可以通过 Redis 提供的2种不同的方式(RDB和AOF)将内存中的数据保存到硬盘中,以此实现数据的持久化,如下图:
谈谈 Redis 持久化_第2张图片

如何持久化

Redis 为我们提供了2种持久化的方式,下面我来分别介绍下。

一、RDB

RDB 属于快照存储,就是在某一时刻将 Redis 当下的内存数据保存到硬盘的文件当中,默认该文件名为 dump.rdb。当 Redis 服务器重启时,会重新将 dump.rdb 文件中的数据加载到内存当中。

触发机制

有三种方式触发 Redis 的 RDB 持久化操作,如下:

  • save 指令
    通过客户端向服务器发送 save 指令,通知服务器执行 RDB 持久化,当服务器接收到指令后,服务器会阻塞 save 指令之后的所有客户端请求,直到数据同步完成,如下图:
    谈谈 Redis 持久化_第3张图片
    如上图,Redis 服务器接收到客户端 alpha 的 save 指令,则客户端 beta、gamma 的指令都将被阻塞。

    很明显,如果快照的数据量比较大,那 Redis 服务器将会有很长时间无法正常接收、响应客户端的新指令,所以,最好不要在生产环境中执行该操作。

  • bgsave 指令
    与 save 指令不同,bgsave 指令对应的是一个异步的操作,当服务器接收到 bgsave 指令后,会启动一个子进程来持久化当前的内存快照,当内存快照保存到 rdb 文件之后,子进程才会退出。在这段时间内,主进程依然可以正常接收其他客户端的请求,如下图:
    谈谈 Redis 持久化_第4张图片
    由于是异步操作,Redis 主进程不会被 RDB 影响,依然可以接收客户端的写指令,但是此时的写指令见不会同步到 Redis 主进程的主内存中,而是会写到一个临时的内存区域作为一个副本,等到 Redis 主进程接收到 RDB 子进程已完成的消息后,再将内存副本中的数据同步到主内存中。

  • 服务器配置自动触发
    以上介绍的 save、bgsave 指令都是需要通过客户端向 Redis 服务器发送相应的指令来实现 Redis持久化的。我们还可以通过设置 Redis 服务器的配置(redis.conf),来指定自动触发 RDB 持久化的条件,比如“多少秒内至少达到多少次写操作”就自动开启 RDB 数据同步,如下面 redis.conf 中的代码片段:

# 900s内至少达到一条写命令
save 900 1
# 300s内至少达至10条写命令
save 300 10
# 60s内至少达到10000条写命令
save 60 10000
RDB 的过程
  1. 执行 bgsave 指令的时候,Redis 主进程会检查是否有正在执行RDB/AOF持久化任务的子进程,如果有的话,直接返回。
  2. Redis 主进程会 fork 一个子进程来执行执行 RDB 操作,fork 操作会对主进程造成阻塞(影响Redis的读写),fork 操作完成后会通知主进程,使主进程不再阻塞。
  3. RDB 子进程会根据 Redis 主进程的内存生成临时的快照文件,RDB 完成后会使用临时快照文件替换掉原来的RDB文件。
二、AOF

AOF 全称为 Append Only File,与 RDB 存储内存内的数据快照不同,AOF 是记录客户端对服务器发送的写操作指令,类似于数据库的审计功能,所有的写操作指令会被追加保存到一个以 aof 为后缀的文件中。当 Redis 服务器重启时,会加载并运行 aof 文件的写操作指令,以达到恢复数据的目的,如下图:
谈谈 Redis 持久化_第5张图片

触发机制

默认情况下,Redis 不会开启 AOF,我们可以通过修改配置文件(redis.conf)来开启AOF并对其进行更加详细的设置,如下:

# 开启aof机制
appendonly yes

# aof文件名
appendfilename "appendonly.aof"

# 写入策略,从“always、everysec、no”中选择一个合适的策略
appendfsync always

# 默认不重写aof文件
no-appendfsync-on-rewrite no

# 保存目录
dir ~/redis/

Redis 提供三种方式向 aof 文件写入信息(写操作指令),如下:

  1. always
    客户端的每一次写操作都会立即保存到 aof 文件中,该策略很安全,但是每个写请求都有 IO 操作的消耗,所以会比较影响速度。
  2. everysec
    每秒写一次 aof 文件,也就是将1秒内发生的所有写操作指令定时写入 aof 文件。
  3. no
    Redis 服务器不负责写入 aof,该任务(什么时候写入 aof 文件)交给操作系统来处理。(注:更快,但也是最不安全的选择,不推荐使用
AOF 的过程
  1. 所有的写指令都会追加到 AOF 缓冲区中。
  2. 使用既定的策略将 AOF 缓冲区中的指令写到 AOF 文件中。
  3. 随着 AOF 文件的越来越大,会对 AOF 文件进行重写。
  4. 当服务器重启的时候,会加载AOF文件并执行AOF文件中的命令用于恢复数据。
AOF 重写

AOF 作为类似于数据库审计功能的存在,会记录客户端所有的操作,例如:对一个 key 多次执行 incr 命令,这时候,aof 也会保存每一次指定到 aof 文件中,这样就会造成 aof 文件非常大,如下:

incr num 1
incr num 2
incr num 3
...
incr num 100000

aof 文件过大,必然会影响加载速度,也就是说当 Redis 服务器重启的时候,会因为 aof 文件很大而造成数据恢复非常慢。为了解决这个问题,Redis 提供了 AOF重写

所谓「AOF重写」,就是将多条指令合并为一条指令,最终生成一个能够恢复当前数据的最少指令集

比如上面的例子中那么多条指令,可以重写为:

incr num 100000

这么看来定期的进行 aof 文件重写,在持久化这个大前提下,还是很有必要的。但是,默认情况下,Redis 是没有开启重写的。那么,我们怎样才能触发重写呢?!

  1. 通过修改配置文件(redis.conf)
    我们可以通过修改 Redis 服务器的 redis.conf 配置文件中 no-appendfsync-on-rewrite 选项的值来指定是否开启 aof 文件重写(如下),这里需要注意的一点是这种方式会在每次 fsync 时都重写,比较影响服务器的性能,因此默认值为 no,不推荐使用。
# 默认不重写aof文件
no-appendfsync-on-rewrite yes
auto-aof-rewrite-min-size 64MB
auto-aof-rewrite-min-percenrage 100
  1. 发送 bgrewriteaof 指令
    由客户端向服务器发送 bgrewriteaof 指令,可以让服务器进行 AOF 重写。

上边说了一下如何开启 AOF 重写,下面再说一下整个 AOF 重写的过程:

  1. 执行 bgrewriteaof 指令的时候,如果当前存在正在执行 AOF 重写的子进程,那么直接返回;如果有正在执行 bgsave 的进程,那么需要等待 bgsave 执行完毕后再执行 AOF 重写。
  2. Redis 主进程会 fork 一个子进程执行 AOF 重写。
  3. AOF 重写过程中,不影响 Redis 原有的 AOF 过程,包括写消息到 AOF 缓存以及同步 AOF 缓存中的数据到硬盘。
  4. AOF 重写过程中,主进程收到的写操作指令的时候,不但会把该指令写到 AOF 缓冲区内,同时还会将该命令写到 AOF 重写缓冲区内。这里需要注意,AOF 缓冲区 和 AOF 重写缓冲区 是两块缓冲区
  5. 由于 AOF 重写过程中原 AOF 文件还在陆续写入数据,所以 AOF 重写子进程只会拿到 fork 子进程时的 AOF 文件进行重写。
  6. AOF 重写子进程将原 AOF 文件中的数据写到一个临时的 AOF 文件中。
  7. 子进程完成 AOF 重写后会通知主进程,主进程得到通知后会将 AOF 重写缓冲区中的数据写入 AOF 缓冲区,然后用新的 AOF 文件替换旧的 AOF 文件。

在整个 AOF 重写过程当中,有三个地方会使 Redis 主进程阻塞:

  1. 主进程 fork 子进程的时候
  2. 主进程把 AOF 重写缓冲区中的数据写到 AOF 缓冲区的时候
  3. 使用新的 AOF 文件替换掉旧的 AOF 文件的时候

你可能感兴趣的:(redis,redis)