ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

Redis 持久化

2021-10-17 16:31:51  阅读:153  来源: 互联网

标签:aof 持久 文件 Redis AOF 快照 日志


Redis 持久化简介

持久化就是把内存的数据写到磁盘中,防止服务器宕机导致内存数据丢失。

Redis 支持两种方式的持久化,一种是RDB的方式,一种是AOF的方式。

RDB 持久化

RDB 就是 Redis DataBase 的缩写,中文名为快照 / 内存快照,RDB持久化是把当前进程数据生成快照保存到磁盘上的过程,由于是某一时刻的快照,那么快照的值要早于或等于内存的值。

触发方式

触发RDB持久化的方式有两种,分别是手动触发和自动触发

手动触发

手动触发分别对应 save 和 bgsave 命令。

  • save命令:阻塞当前 Redis 服务器,直到 RDB 过程完成为止,对于内存比较大的实例会造成长时间的阻塞,线上环境不建议使用。
  • bgsave命令:Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由 子进程 负责,完成后自动结束。阻塞只发生在 fork 阶段,一般时间很短。

bgsave流程图如下所示:

执行过程:

  • 执行bgsave命令
  • Redis 父进程判断当前是否存在正在执行的子进程,如果存在,bgsave命令直接返回。
  • 父进程执行fork操作创建子进程fork 操作过程中父进程会阻塞
  • 父进程 fork 完成后,父进程继续接收并处理客户端的请求,而子进程开始将内存中的数据写进硬盘的临时数据
  • 当子进程写完所有数据后会用该临时文件替换旧的RDB文件

自动触发

在以下四种情况时会自动触发。

  • redis.conf 中配置 save m n ,即在 m 秒内有 n 次修改时,自动触发 bgsave 生成 rdb 文件。
  • 主从复制时, 从节点 要从 主节点 进行 全量复制 时,也会触发bgsave操作,生成当时的快照发送到从节点。
  • 执行 debug reload 命令重新加载 redis 时如果没有开启 aof 持久化,那么也会触发 bgsave
  • 默认情况下执行 shutdown 命令时,如果没有开启 aof 持久化,那么也会触发 bgsave 操作。

深入理解

  • 由于生产环境中我们为Redis开辟的内存区域都比较大(例如6GB),那么将内存中的数据同步到硬盘的过程可能就会持续比较长的时间,而实际情况是这段时间Redis服务一般都会收到数据写操作的请求。那么如何保证数据一致性呢?

RDB 中的核心思路是 Copy-on-Write (写时复制),来保证在进行快照操作的这段时间,需要压缩写入磁盘上的数据在内存中不会发生变化。在正常的快照操作中,一方面 Redis 主进程 会 fork 一个新的快照进程专门来做这个事情,这样保证了 Redis 服务 不会停止对客户端包括写请求在内的任何响应。另一方面,在 fork 过程中发生的数据变化会以副本的方式存放在另一个新的内存区域,待快照操作结束后才会同步到原来的内存区域。

举个例子:如果主线程对这些数据也都是读操作(例如图中的键值对A),那么,主线程 和 bgsave 子进程相互不影响。但是,如果主线程要修改一块数据(例如图中的键值对C),那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入到 rdb 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。

  • 在进行快照操作的这段时间,如果发生服务崩溃怎么办?

在没有将数据全部写入到磁盘前,这次快照操作都不算成功。如果出现了服务崩溃的情况,将以上一次完整的 RDB 快照文件 作为回复内存数据的参考。也就是说,在快照操作过程中不能影响上一次的备份数据。Redis服务会在磁盘上创建一个临时文件进行数据操作,待操作成功后才会用这个临时文件替换掉上一次的备份。

  • 可以每秒做一次快照吗?

显然是不可以的,虽然 bgsave 执行时不会阻塞主线程,但是,如果频繁的执行全量快照,也会带来两方面的开销:

  • 频繁的将全量数据写入磁盘,会给磁盘带来很大的压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环。
  • bgsave 子进程需要通过 fork 操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork 这个创建进程的操作本身 会阻塞主线程,而且主线程的内存越大,阻塞时间越长。如果频繁的 fork 出 bgsave 子进程,这就会频繁的阻塞主线程了。

RDB优缺点

  • 优点:

    • Redis 加载 RDB 文件恢复数据要远远快于 AOF 方式。
    • RDB 文件是某个时间节点的快照,默认使用LZF算法进行压缩,压缩后的文件体积远远小于内存大小,适用于备份、全量复制等场景。
  • 缺点:

    • RDB 方式的实时性不够,无法做到秒级持久化
    • 每次调用 bgsave 都需要 fork子进程,fork子进程 属于重量级操作,频繁执行成本较高
    • RDB 文件是 二进制 的,没有可读性,AOF 文件在了解其结构的情况下可以手动修改或者补全。
    • 版本兼容RDB文件问题,Redis 版本升级过程中有多个格式的 RDB 版本,存在老版本 Redis 无法兼容 新版 RDB格式的问题

AOF 持久化

Redis 是“写后”日志,Redis 先执行命令,把数据写入内存,然后才记录日志。日志里记录的是Redis收到的每一条命令,这些命令是以文本形式保存。

PS:大多数的数据库采用的是写前日志(WAL),例如MySQL,通过写前日志和两阶段提交,实现数据和逻辑的一致性。

而 AOF 日志采用写后日志,即先写内存,后写日志

为什么采用写后日志?

Redis 要求高性能,采用写日志的有两方面好处

  • 避免额外的检查开销:Redis 在向AOF里面记录日志的时候,并不会先对这些命令进行语法检查。所以,如果先记日志再执行命令的话,日志中就有可能记录了错误的命令,Redis在使用日志恢复数据时,就可能会出错。
  • 不会阻塞当前的写操作。

这种方式存在一定风险:

  • 如果命令执行完成,写日志之前宕机了,会丢失数据。
  • 主线程写磁盘压力大,导致写盘慢,阻塞后续操作。

AOF的实现

AOF日志记录 Redis 的每个写指令,步骤分为:命令追加(append)、文件写入(write)和 文件同步(sync)。

  • 命令追加:当 AOF 持久化功能打开时,服务器在执行完一个 写命令 之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。
  • 文件写入和同步:关于何时将 aof_buf 缓冲区的内容写入 AOF 文件中,Redis提供了三种写回策略:

Always同步写回:每个写命令执行完,立马同步地将日志写回磁盘;

Everysec每秒写回:每个写命令执行完,只是先把日志写到 AOF文件 的内容缓冲区,每隔 1s 把缓冲区的内容写入磁盘;

No操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内容缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。

  • 三种写回策略的优缺点

上面的三种策略体现了一个重要原则:trade-off,取舍,指在性能和可靠性保证之间做取舍。

关于 AOF 的同步策略是涉及到操作系统的 write 函数 和 fsync 函数的,在《Redis设计与实现》是这样说明的。

为了提高文件写入效率,在现代操作系统中,当用户调用write函数,将一些数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区的空间被填满或超过了指定时限后,才真正将缓冲区的数据写入到磁盘里。

这样的操作虽然提高了效率,但也为数据写入带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失。为此,系统提供了fsync、fdatasync同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘中,从而确保写入数据的安全性。

深入理解AOF重写

Redis 通过创建一个新的 AOF 文件来替换现有的 AOF ,新旧两个 AOF 文件保存的数据相同,但新 AOF 文件中没有冗余命令。

  • AOF 重写会阻塞吗?

AOF重写过程是由后台进程bgrewriteaof来完成的。主线程 fork 出后台的 bgrewriteaof 子进程,fork 会把主线程的内存拷贝一份给bgrewriteaof 子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof 子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。

所以 AOF 在重写时,在 fork 进程时是会阻塞主线程的。

  • AOF 日志何时会重写?

有两个配置项控制AOF重写的触发:

  1. auto-aof-rewrite-min-size:表示运行AOF重写时 文件的最小大小,默认为64MB。
  2. auto-aof-rewrite-percentage:这个值的计算方式是,当前 aof 文件大小和上一次重写后 aof 文件大小的差值,再除以上一次重写后 aof 文件大小。也就是当前 aof文件 比上一次重写后 aof文件 的增量大小,和上一次重写后 aof文件 大小的比值。
  • 重写日志时,有新数据写入怎么处理?

重写过程总结为:“一个拷贝,两处日志”。在 fork 出子进程时的拷贝,以及在重写时,如果有新数据写入,主线程就会将命令记录到两个 aof 日志内存缓冲区中。如果 AOF 写回策略配置的是 always ,则直接将命令写回旧的日志文件,并且保存一份命令至 AOF 重写缓冲区,这些操作对新的日志文件是不存在影响的。(旧的日志文件:主线程使用的日志文件,新的日志文件: bgrewriteaof 进程使用的日志文件)。

而在 bgrewriteaof 子进程完成对日志文件的重写操作后,会提示主线程已经完成重写操作,主线程会将 AOF 重写缓冲区中的命令追加到新的日志文件后面。这时候在高并发的情况下,AOF 重写缓冲区积累可能会很大,这样就会造成阻塞,Redis后来通过 Linux 管道技术让 aof 重写期间就能同时进行回放,这样 aof 重写结束后只需回放少量剩余数据即可。

最后通过修改文件名的方式,保证文件切换的原子性。

在 AOF 重写日志期间发生宕机的话,因为日志文件还没切换,所以恢复数据时,用的还是旧的日志文件。

从持久化中恢复数据

流程如下:

  • redis 重启时判断是否开启 aof ,如果开启 aof ,那么就优先加载 aof 文件;
  • 如果 aof 存在,那么就去加载 aof 文件,加载成功的话 redis 重启成功,如果 aof 文件加载失败,那么就会打印日志表示启动失败,此时可以去修复 aof 文件后重新启动;
  • 若 aof 文件不存在,那么 redis 就会转去加载 rdb 文件,如果 rdb 文件不存在,redis 直接启动成功;
  • 如果 rdb 文件存在就会去加载 rdb 文件恢复数据,若加载失败则打印日志提示启动失败,若加载成功,那么 redis 重启成功,且使用 rdb 文件恢复数据;

那么为什么会优先加载 aof 文件呢?

因为 aof 保存的数据更完整,因为 aof 基本上最多损失 1s 的数据。

标签:aof,持久,文件,Redis,AOF,快照,日志
来源: https://www.cnblogs.com/Cherish486/p/15417203.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有