博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Redis(二)Redis持久化(RDB+AOF)详解
阅读量:1905 次
发布时间:2019-04-26

本文共 8577 字,大约阅读时间需要 28 分钟。

文章说明:文章源于狂神B站视频内容,但觉得其中稍微简单了一点(未细讲原理),因此找到了掘金上的两篇RDB和AOF详解文章进行了整合,希望能帮助更好的理解redis的持久化机制。

文章目录

  Redis 是一种内存数据库,是将所有数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,官方表示,redis是基于内存操作,CPU不是redis性能瓶颈,redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程(Redis6.0之前,6.0之后可以为多线程)了!多线程(CPU上下文切换,耗费资源操作)对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存充足情况下这个就是最佳的方案。因此redis读写效率要比传统的将数据保存在磁盘上的数据库要快很多(CPU>内存>硬盘)。但是一旦进程退出(比如断电),Redis 的数据就会丢失。

  为了解决这个问题,Redis 提供了 RDB 和 AOF 两种持久化方案,将内存中的数据保存到磁盘中,避免数据丢失。

antirez 在《Redis 持久化解密》一文中说,一般来说有三种常见的策略来进行持久化操作,防止数据损坏:

  • 方法1 是数据库不关心发生故障,在数据文件损坏后通过数据备份或者快照来进行恢复。Redis 的 RDB 持久化就是这种方式。

  • 方法2 是数据库使用操作日志,每次操作时记录操作行为,以便在故障后通过日志恢复到一致性的状态。因为操作日志是顺序追加的方式写的,所以不会出现操作日志也无法恢复的情况。类似于 Mysql 的 redo 和 undo 日志,具体可以看这篇《InnoDB的磁盘文件及落盘机制》文章。

  • 方法3 是数据库不进行老数据的修改,只是以追加方式去完成写操作,这样数据本身就是一份日志,这样就永远不会出现数据无法恢复的情况了。CouchDB就是此做法的优秀范例。

一、RDB持久化

  RDB(Redis DataBase) 就是第一种方法,它就是把当前 Redis 进程的数据生成时间点快照( point-in-time snapshot ) 保存到存储设备的过程。

RDB的触发机制
  RDB 触发机制分为使用指令手动触发和 redis.conf 配置自动触发。手动触发 Redis 进行 RDB 持久化的指令的为:

  • save,该指令会阻塞当前 Redis 服务器,执行 save 指令期间,Redis 不能处理其他命令,直到 RDB 过程完成为止。

  • bgsave,执行该命令时,Redis 会在后台异步执行快照操作,此时 Redis 仍然可以响应客户端请求。具体操作是 Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。Redis 只会在 fork 期间发生阻塞,但是一般时间都很短。但是如果 Redis 数据量特别大, fork 时间就会变长,而且占用内存会加倍,这一点需要特别注意。

自动触发 RDB 的默认配置如下所示:

save 900 1 # 如果 900s 内,至少有 1 个 key 进行了修改,进行持久化操作save 300 10 # 如果 300s 内,至少有 10 个 key 进行了修改,进行持久化操作save 60 10000  # 如果 60s 内,至少有 10000个 key 进行了修改,进行持久化操作

在这里插入图片描述

  上图表明了三种触发 RDB 持久化的手段之间的整体关系。通过 serverCron 自动触发的 RDB 相当于直接调用了 bgsave 指令的流程进行处理。而 bgsave 的处理流程启动子进程后,调用了 save 指令的处理流程。

RDB持久化机制配置

# 如果900s内,如果至少有一个1个key进行了修改,就进行持久化操作 save 900 1 # 如果300s内,如果至少10个key进行了修改,就进行持久化操作 save 300 10 # 如果60s内,如果至少10000个key进行了修改,就进行持久化操作 save 60 10000 # 关闭该规则使用save “ ” # yes代表当使用bgsave命令持久化出错时候停止写RDB快照文件,no表明忽略错误继续写文件。stop-writes-on-bgsave-error yes   # # 是否压缩 rdb 文件,需要消耗一些cpu资源,该功能可以节约磁盘空间。rdbcompression yes                # 在写入文件和读取文件时是否开启rdb文件检查,检查是否有无损坏,如果在启动是检查发现损坏,则停止启动rdbchecksum yes# 持久化文件名dbfilename dump.rdb       # 数据文件存放目录,rdb快照文件和aof文件都会存放至该目录,请确保有写权限        dir ./

在这里插入图片描述

在指定的时间间隔内,将内存中的数据集快照写入磁盘,也就是 Snapshot 快照,它恢复时是将快照文件直接读取到内存里的。

  Redis 会单独创建(fork)一个子进程进行持久化,会先将数据写入一个临时文件中,待持久化过程结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程不进行任何 IO 操作,这就确保的极高的性能。如果需要大规模的数据的恢复,且对数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加高效。RDB 唯一的缺点是最后一次持久化的数据可能会丢失。

  为什么 Redis 使用子进程而不是线程来进行后台 RDB 持久化呢?主要是出于Redis性能的考虑,我们知道Redis对客户端响应请求的工作模型是单进程和单线程的,如果在主进程内启动一个线程,这样会造成对数据的竞争条件。所以为了避免使用锁降低性能,Redis选择启动新的子进程,独立拥有一份父进程的内存拷贝,以此为基础执行RDB持久化。

  但是需要注意的是,fork 会消耗一定时间,并且父子进程所占据的内存是相同的,当 Redis 键值较大时,fork 的时间会很长,这段时间内 Redis 是无法响应其他命令的。除此之外,Redis 占据的内存空间会翻倍。

二、AOF持久化

  当redis存储非临时数据时,为了降低redis故障而引起的数据丢失,redis提供了AOF(Append Only File)持久化,从单词意思讲,将命令追加到文件。AOF可以将Redis执行的每一条写命令追加到磁盘文件(appendonly.aof)中。在redis启动时候优先选择从AOF文件恢复数据。由于每一次的写操作,redis都会记录到文件中,所以开启AOF持久化会对性能有一定的影响。

AOF持久化以独立日志的方式记录每次写命令,并在 Redis 重启时在重新执行 AOF 文件中的命令以达到恢复数据的目的。AOF 的主要作用是解决数据持久化的实时性。
antirez 在《Redis 持久化解密》一文中讲述了 RDB 和 AOF 各自的优缺点:

  • RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据备份。非常适合备份,全量复制等场景。比如每6小时执行 bgsave 备份,并把 RDB 文件拷贝到远程机器或者文件系统中,用于灾难恢复。
  • Redis 加载 RDB 恢复数据远远快于 AOF 的方式
  • RDB 方式数据没办法做到实时持久化,而 AOF 方式可以做到。

AOF持久化数据丢失更少,其消耗内存更少(RDB方式执行bgsave会有内存拷贝)

开启AOF持久化
  默认情况下,redis是关闭了AOF持久化,开启AOF通过配置appendonly为yes开启。将appendonly修改为yes,开启aof持久化机制,默认会在目录下产生一个appendonly.aof文件。修改配置文件或者在命令行直接使用config set修改,再用config rewrite同步到配置文件。通过客户端修改的好处是不用重启redis,AOF持久化直接生效。

config get appendonly 查询配置状态config set appendonly yes 修改配置,命令生效了,但配置文件里没有生效需要通过config rewrite 重写入到redis.conf中,配置文件才生效

在这里插入图片描述

可以看到,配置文件中
在这里插入图片描述

AOF持久化配置

appendonly no   # 默认是不开启aof模式的,默认使用rdb方式进行持久化,大部分情况rdb够用,开启的话改为yes即可appendfilename "appendonly.aof"  # 持久化的文件的名字appendfsync always       # 每执行一次更新命令,持久化一次  消耗性能appendfsync everysec     # 每秒钟持久化一次,可能会丢失这 1s 的数据,因为这一秒redis不能执行其他操作appendfsync no           # 不持久化,不执行sync,这个时候操作系统自己同步数据,速度最快

AOF文件错误

  如果 aof 文件有错误,这时候 redis 是启动不起来的,我们需要修复这个aof文件 redis 给我们提供了一个工具 redis-check-aof --fix 。

redis-check-aof --fix appendonly.aof

这个工具可以修复错误,但是可能会造成一部分数据丢失(据我观察貌似是删除出现错误的那部分数据)。

下面,我们就来了解一下 AOF 是如何做到实时持久化的。
在这里插入图片描述
AOF以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件 但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件 的内容将写指令从前到后执行一次以完成数据的恢复工作。简单一点我们就可以看下面这张图:
在这里插入图片描述
如上图所示,AOF 持久化功能的实现可以分为命令追加( append )、文件写入( write )、文件同步( sync )、文件重写(rewrite)重启加载(load)。其流程如下:

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

命令追加

  当 AOF 持久化功能处于打开状态时,Redis 在执行完一个写命令之后,会以协议格式(也就是RESP,即 Redis 客户端和服务器交互的通信协议 )将被执行的写命令追加到 Redis 服务端维护的 AOF 缓冲区末尾。
比如说 SET mykey myvalue这条命令就以如下格式记录到 AOF 缓冲中。

"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"

Redis 协议格式本文不再赘述,AOF之所以直接采用文本协议格式,是因为所有写入命令都要进行追加操作,直接采用协议格式,避免了二次处理开销。

文件写入和同步

Redis 每次结束一个事件循环之前,它都会调用 flushAppendOnlyFile函数,判断是否需要将 AOF 缓存区中的内容写入和同步到 AOF 文件中。
flushAppendOnlyFile函数的行为由 redis.conf 配置中的 appendfsync选项的值来决定。该选项有三个可选值,分别是 always、everysecno

  • always:Redis 在每个事件循环都要将 AOF 缓冲区中的所有内容写入到 AOF 文件,并且同步 AOF 文件,所以 always 的效率是 appendfsync 选项三个值当中最差的一个,但从安全性来说,也是最安全的。当发生故障停机时,AOF 持久化也只会丢失一个事件循环中所产生的命令数据。
  • everysec:Redis 在每个事件循环都要将 AOF 缓冲区中的所有内容写入到 AOF 文件中,并且每隔一秒就要在子进程中对 AOF 文件进行一次同步。从效率上看,该模式足够快。当发生故障停机时,只会丢失一秒钟的命令数据。
  • no:Redis 在每一个事件循环都要将 AOF 缓冲区中的所有内容写入到 AOF 文件。而 AOF 文件的同步由操作系统控制。这种模式下速度最快,但是同步的时间间隔较长,出现故障时可能会丢失较多数据。

Linux 系统下 write操作会触发延迟写( delayed write )机制。Linux 在内核提供页缓存区用来提供硬盘 IO 性能。write 操作在写入系统缓冲区之后直接返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区页空间写满或者达到特定时间周期。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。

fsync针对单个文件操作,对其进行强制硬盘同步,fsync 将阻塞直到写入磁盘完成后返回,保证了数据持久化。
appendfsync的三个值代表着三种不同的调用 fsync的策略。调用fsync周期越频繁,读写效率就越差,但是相应的安全性越高,发生宕机时丢失的数据越少。
AOF 数据恢复
  AOF 文件里边包含了重建 Redis 数据所需的所有写命令,所以 Redis 只要读入并重新执行一遍 AOF 文件里边保存的写命令,就可以还原 Redis 关闭之前的状态。
在这里插入图片描述
Redis 读取 AOF 文件并且还原数据库状态的详细步骤如下:

  • 创建一个不带网络连接的的伪客户端( fake client),因为 Redis 的命令只能在客户端上下文中执行,而载入 AOF 文件时所使用的的命令直接来源于 AOF 文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行 AOF 文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样的。
  • 从 AOF 文件中分析并取出一条写命令。
  • 使用伪客户端执行被读出的写命令。
  • 一直执行步骤 2 和步骤3,直到 AOF 文件中的所有写命令都被处理完毕为止。

当完成以上步骤之后,AOF 文件所保存的数据库状态就会被完整还原出来。

AOF 重写
  因为 AOF 持久化是通过保存被执行的写命令来记录 Redis 状态的,所以随着 Redis 长时间运行,AOF 文件中的内容会越来越多,文件的体积也会越来越大,如果不加以控制的话,体积过大的 AOF 文件很可能对 Redis 甚至宿主计算机造成影响。
在这里插入图片描述
  如上图,如果 aof 文件大于 64m,太大了。 Redis父进程会fork一个新的进程来将我们的文件进行重写。
  为了解决 AOF 文件体积膨胀的问题,Redis 提供了 AOF 文件重写( rewrite) 功能。通过该功能,Redis 可以创建一个新的 AOF 文件来替代现有的 AOF 文件。新旧两个 AOF 文件所保存的 Redis 状态相同,但是新的 AOF 文件不会包含任何浪费空间的荣誉命令,所以新 AOF 文件的体积通常比旧 AOF 文件的体积要小得很多。
在这里插入图片描述
  如上图所示,重写前要记录名为list的键的状态,AOF 文件要保存五条命令,而重写后,则只需要保存一条命令。
  AOF 文件重写并不需要对现有的 AOF 文件进行任何读取、分析或者写入操作,而是通过读取服务器当前的数据库状态来实现的。首先从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令,这就是 AOF 重写功能的实现原理。
  在实际过程中,为了避免在执行命令时造成客户端输入缓冲区溢出,AOF 重写在处理列表、哈希表、集合和有序集合这四种可能会带有多个元素的键时,会先检查键所包含的元素数量,如果数量超过 REDIS_AOF_REWRITE_ITEMS_PER_CMD( 一般为64 )常量,则使用多条命令记录该键的值,而不是一条命令。
rewrite的触发机制主要有一下三个:

  • 手动调用bgrewriteaof命令,如果当前有正在运行的 rewrite 子进程,则本次rewrite 会推迟执行,否则,直接触发一次 rewrite。
  • 通过配置指令手动开启 AOF 功能,如果没有 RDB 子进程的情况下,会触发一次 rewrite,将当前数据库中的数据写入 rewrite 文件。
  • 在 Redis 定时器中,如果有需要退出执行的 rewrite 并且没有正在运行的 RDB 或者 rewrite 子进程时,触发一次或者 AOF 文件大小已经到达配置的 rewrite 条件也会自动触发一次。

AOF 后台重写

  AOF 重写函数会进行大量的写入操作,调用该函数的线程将被长时间阻塞,所以 Redis 在子进程中执行 AOF 重写操作。

  • 子进程进行 AOF 重写期间,Redis 进程可以继续处理客户端命令请求。
  • 子进程带有父进程的内存数据拷贝副本,在不适用锁的情况下,也可以保证数据的安全性。

  但是,在子进程进行 AOF 重启期间,Redis接收客户端命令,会对现有数据库状态进行修改,从而导致数据当前状态和 重写后的 AOF 文件所保存的数据库状态不一致。

  为此,Redis 设置了一个 AOF 重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用,当 Redis 执行完一个写命令之后,它会同时将这个写命令发送给 AOF 缓冲区和 AOF 重写缓冲区。

在这里插入图片描述

当子进程完成 AOF 重写工作之后,它会向父进程发送一个信号,父进程在接收到该信号之后,会调用一个信号处理函数,并执行以下工作:

  • 将 AOF 重写缓冲区中的所有内容写入到新的 AOF 文件中,保证新 AOF 文件保存的数据库状态和服务器当前状态一致。
  • 对新的 AOF 文件进行改名,原子地覆盖现有 AOF 文件,完成新旧文件的替换
  • 继续处理客户端请求命令。

在整个 AOF 后台重写过程中,只有信号处理函数执行时会对 Redis 主进程造成阻塞,在其他时候,AOF 后台重写都不会阻塞主进程。

在这里插入图片描述
优点

1、每一次修改都同步,文件的完整性更加好

2、每秒同步一次,可能会丢失一秒的数据

3、从不同步,效率最高的

缺点

1、相对于数据文件来说, AOF 远远大于 RDB ,修复的速度也比 RDB 慢

2、AOF 的运行效率也比 RDB 慢,所以 Redis 默认的配置就是 RDB 持久化。

三、持久化扩展

RDB-AOF混合持久化:通过aof-use-rdb-preamble配置参数控制,yes则表示开启,no表示禁用,默认是禁用的,可通过config set修改。

持久化扩展

1、RDB 持久化方式能够在指定的时间间隔内对你的数据进行快照存储。

2、AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始 的数据,AOF命令以Redis 协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重 写,使得AOF文件的体积不至于过大。

3、只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化。

4、同时开启两种持久化方式

在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF 文件保存的数据集要比RDB文件保存的数据集要完整。

RDB 的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要只使用AOF呢?建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有 AOF可能潜在的Bug,留着作为一个万一的手段。

5、性能建议

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

  如果Enable AOF ,好处是在恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自 己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite 的后将 rewrite 过程中产 生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite 的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重 写可以改到适当的数值。

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

文章来源:

1、
2、
3、

转载地址:http://syncf.baihongyu.com/

你可能感兴趣的文章
linux进程信号捕获及删除捕获
查看>>
linux重启停止的作业
查看>>
linux中设置每月最后一天执行crontab
查看>>
linux中gawk命令
查看>>
Linux命令sed多组命令集
查看>>
查找linux里的文件匹配行号的另一种方式
查看>>
BRE特殊字符组,正则表达式
查看>>
linux命令gawk指定区间范围
查看>>
linux逆序输出文件的内容
查看>>
linux给文件输出行号
查看>>
删除连续的空白行,删除开头的空白行,删除结尾的空白行
查看>>
oracle数据库全局性HANG的处理过程
查看>>
mount: unknown filesystem type ‘ntfs’ 问题
查看>>
数据库局部性HANG处理过程
查看>>
oracle怎样快速定位资源持有者
查看>>
oracle数据库在线日志文件损坏的处理思路
查看>>
oracle监听的常用命令
查看>>
oracle密码带特殊字符,如”@“号,在imp,exp里的写法
查看>>
oracle监听配置注意点
查看>>
linux下一个网卡配置多个ip【虚拟ip】
查看>>