Redis学习之持久化机制

原文博客地址: pjmike的博客

前言

持久化就是将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命令

127.0.0.1:6379> save
OK
复制代码

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

bgsave

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

扫描二维码关注公众号,回复: 3984523 查看本文章

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 默认的自动触发机制如下:

# 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的相关配置,如下所示:

# 快照的文件名
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持久化

# 是否开启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配置文件中,自动触发参数设置如下:

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

重新加载

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

redis_aof_reload

参考资料 & 鸣谢

猜你喜欢

转载自juejin.im/post/5be2d829e51d450aa46c770f