聊聊 Redis 高可用之持久化AOF和RDB分析

2023-05-16

Redis 持久化概述

Redis 是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将 Redis 中的数据以某种形式把内存中的数据保存到磁盘中;当 Redis 重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。

Redis 提供了两种不同的持久化方法来讲数据存储到硬盘上 :

  • RDB:一种称为快照的方式,是 将某一时刻内存中的所有数据以快照的形式写入硬盘上。
  • AOF:一种称为只追加文件的方式,它会是将每次执行的写命令以追加的形式保存到硬盘上。

下面依次介绍 RDB 持久化和 AOF 持久化。

RDB 持久化

RDB ( Redis Database ) 持久化就是把存储在内存里的数据在某个时间点上写入到硬盘上。在创建快照之后,用户可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本,也可以当Redis 重新启动时,读取快照文件恢复数据。

1 创建快照的方式

  • 可以在客户端通过向 Redis 发送 BGSAVE 命令创建快照。接到命令后,Redis 会调用 fork 来创建一个子进程,然后子进程负责将快照写入硬盘,而父进程可以继续处理命令请求。
  • 可以在客户端通过向 Redis 发送 SAVE 命令来创建快照,接到 SAVE 命令后,Redis 服务器直接在主进程中创建快照,在创建快照期间,Redis 服务不再响应任何其他命令。
  • redis.conf 配置文件中设置了 save 配置选项, 比如 save 60 10000, 是指当“60 秒之内有10000次写入” 这个条件满足是,Redis 会自动触发 BGSAVE 命令。如果设置了多个 save 配置选项,那么当任意一个满足是,Redis 就会触发一次 BGSAVE 命令。
  • 当 Redis 接收到 SHUTDOWN 命令关闭服务器请求时,或者接收到标准 TERM 信号时,会执行 SAVE 命令,阻塞所有客户端请求,并在 SAVE 命令执行完毕之后关闭服务器。
  • 当一个 Redis 服务器第一次连接另一个 Redis 服务器,并向对方发送 psync 命令来开始复制操作时,如果主服务当前没有正在执行 BGSAVE 操作,那么主服务器就会执行 BGSAVE 命令。

2 原理分析

上文我们提到了创建 RDB 快照的几种方式,总结可以发现这几种方式无非就是通过 SAVEBGSAVE 命令来生成快照。

  • SAVE 命令: 此命令是在 Redis 服务器的主进程中执行,我们知道 Redis 是单线程的,那么,在执行此命令是就会阻塞当前 Redis 服务器,而无法执行其他命令的请求,对于内存比较大的实例会造成长时间阻塞。
  • BGSAVE 命令:此命令是 Redis 执行 fork 操作创建子进程,RDB 持久化过程有子进程处理。阻塞只发生在 fork 阶段,fork 结束后,Redis 主进程还可以继续处理其他命令请求。

由于 SAVE 命令会造成长时间阻塞,而 BGSAVE 命令阻塞时间较短,所以,一般很少使用SAVE 命令,而是用BGSAVE 命令,接下来我们进一步讲解一下 BGSAVE 命令。

虽然 SAVE 命令一直阻塞 Redis 直到快照生成完毕, 但是因为它不需要创建子进程, 并且没有子进程争抢资源,所以 SAVE 创建快照的速度比 BGSAVE 创建快照的速度快些。

fork系统调

fork 系统调用会产生一个子进程,它与父进程共享相同的内存地址空间,这样子进程在这一时刻就能拥有与父进程的相同的内存数据。

虽然子进程与父进程共享同一块内存地址空间,但是在 fork 子进程时,操作系统需要拷贝父进程的内存页表给子进程,如果整个 Redis 实例内存占用很大,那么它的内存页也会很大,在拷贝是就会比较耗时,同时这个过程会消耗大量的 CPU 资源。在完成拷贝之前父进程也处于阻塞状态,无法处理客户端请求。

fork 执行完之后,子进程就可以扫描自身所有的内存数据,然后把全部数据写入到 RDB 文件中。

fork 操作的流程如下所示:

在这里插入图片描述

Copy On Write

前面介绍了 BGSAVE 命令不会造成阻塞主进程接收其他命令的请求,有 fork 系统调用可知,子进程共享主进程的内存数据,那么,当 Redis 服务器接收到写命令时,修改内存数据时,子进程读取同一内存地址的数据时,就会出现脏数据。

如果为了快照而暂停写操作,肯定是不能接受的。所以,Redis 借助了操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。

当父进程接收到写操作时,如果要修改的数据在内存中已经存在,那么,主进程就会拷贝一份数据,重新分配新的内存地址空间,这样,父进程就在新申请的内存空间中修改数据,不在与子进程共享,这个过程就是 Copy On Write(写实复制)。

比如我们修改上图的 物理页13 的数据,Copy On Write 后的图如下所:

在这里插入图片描述

这样父子进程的内存就会逐渐分离,父进程申请新的内存空间并更改内存数据,子进程的内存数据不受影响。

由此可以看出,在生成 RDB 文件时,不仅消耗 CPU 资源,还有需要占用最多一倍的内存空间。所以,我们应该保证 Redis 机器拥有 足够的CPU和内存资源,并合理设置生成 RDB 的时机。

3 RDB 的优缺点

RDB 的优点:

  • RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据快照。非常适用于备份,全量复制等场景。
  • RDB 是派生子进程来生成快照文件,最大限度的减少了父进程阻塞时间,从而 RDB 最大限度的提高了 Redis 的性能。
  • Redis 加载 RDB 恢复数据要比 AOF 快的多。

RDB 的缺点:

  • RDB 方式没办法做到实时持久化 / 秒级持久化。会造成数据丢失。
  • RDB 需要 fork 创建子进程,进行持久化,如果数据集很大,CPU 性能不好,可能会导致 Redis 阻塞服务几毫秒甚至一秒钟,频繁执行成本过高。

AOF 持久化

AOF 全称成为 Append Only File ( 只追加文件)。简单来说,AOF 持久化会把执行的写命令写到 AOF 文件的末尾,以此来记录数据发生的变化。因此,Redis 只要从头到尾执行一次 AOF 文件中的文件,就可以恢复数据。

1 开启 AOF

Redis 中 AOF 默认是关闭的,在 redis.conf 配置文件中添加如下配置开启。

# 开启AOF
appendonly yes

# AOF文件名
appendfilename "appendonly.aof"

# 文件刷盘方式
appendfsync everysec

2 文件同步

开启 AOF 后,所有的写入命令都会追加到 AOF缓冲区 中, Redis 会根据刷盘策略把 AOF 缓冲区中的数据保存到磁盘中,为了保证数据文件的安全性,Redis 提供了如下刷盘策略:

  • appendfsync always:每个 Redis 写命令都会被写入硬盘,在一般 SATA 硬盘上,Redis 只能支持大约几百的 TPS 写入,对性能影响大;而 SSD 硬盘能处理几万的TPS,但是频繁的吸入会造成 SSD 硬盘的寿命缩短。所以 always 占用磁盘 I/O 比较高,数据安全性高。
  • appendfsync everysec:Redis 每秒对 AOF 文件进行同步一次。对性能影响较小,Redis 宕机时最多丢失 1 秒钟的数据。一般建议配置此种刷盘策略。
  • appendfsync no:按照操作系统的机制刷盘,对性能影响最小,数据安全性低,Redis 宕机丢失数据取决于操作系刷盘机制。

3 重写/压缩 AOF 文件

随着命令不断写入 AOF,文件会越来越大,为了解决这个问题,Redis 引入了 AOF 重写机制压缩文件体积。AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新的 AOF文件的过程。

文件变小原因

  1. 进程内一超时的数据不再写入文件。
  2. 旧文件含有的无效命令,重写使用内存数据直接生成,这样新文件中只保留最终数据的写入命令。
  3. 多条写命令合并为一个。

触发方式

AOF 重写可以手动触发和自动触发:

  • 手动触发:直接调用 BGREWRITEAOF命令。

  • 自动触发:根据 auto-aof-rewrite-percentageauto-aof-rewrite-min-size 参数确定自动触发机制,当满足条件时,就会调用 Redis 的 BGREWRITEAOF 命令。

    // AOF文件距离上次文件增长超过多少百分比则触发重写
    auto-aof-rewrite-percentage 100
    // AOF文件体积最小多大以上才触发重写
    auto-aof-rewrite-min-size 64mb

重写机制

BGREWRITEAOF 命令 与 BGSAVE 命令相似,在执行 BGREWRITEAOF 命令后,父进程 调用 fork 创建一个子进程执行 AOF 的重写操作。流程如下所示:

在这里插入图片描述

流程说明:
1、执行 AOF 重写请求

2、父进程执行 fork 创建子进程

3.1、fork 完成后,主进程继续响应其他命令,所有修改命令写入 AOF 缓冲区

3.2、接收的写命令同时也写入 AOF 重写缓冲区中。

4、子进程根据内存快照,按照命令合并规则写入到新的 AOF 文件。

5.1、新 AOF文件写完成后,子进程发送信号给父进程。

5.2、父进程把 AOF 重写缓冲区的数据写入到新的 AOF 文件

5.3、使用新 AOF 文件替换老文件,完成 AOF 重写。

4 AOF 追加阻塞

当开启 AOF 持久化时,常用的同步硬盘策略是 everysec ,用于平衡性能和数据安全性。对于这种方式,Redis 使用另一个线程执行 fsync 同步刷盘。当系统硬盘资源繁忙是,会造成 Redis 主线程阻塞。

阻塞流程

  1. 主线程负责写入 AOF 缓冲区。
  2. AOF 线程负责每秒执行一次同步磁盘操作,并记录最近一次同步时间。
  3. 主线程负责对比上次 AOF 同步时间:
    • 如果距上次同步成功时间在 2 秒内,主线程直接返回。
    • 如果距上次同步成功时间超过 2 秒,主线程将会阻塞,直到同步操作完成。

阻塞流程发现问题

  1. everysec 配置最多可能丢失 2 秒数据,而不是 1 秒。
  2. 如果系统 fsync 缓慢,将会导致 Redis 主线程阻塞影响效率。

5 AOF 优缺点

AOF优点:

  1. AOF 可以更好的保护数据不丢失,一般 AOF 会以每隔 1 秒,通过后台的一个线程去执行一次 fsync 操作,如果 Redis 挂掉了,最多丢失 1 秒的数据。
  2. AOF 以 append-only 的模式写入,所以没有任何的磁盘寻址的开销,写入性能非常的高。
  3. AOF 日志文件的命令通过非常可读的方式进行记录,这个非常适合做灾难性的误删除紧急恢复,如果某人不小心用 flushall 命令清空了所有数据,只要这个时候还没有执行 Rewrite,那么就可以将日志文件中的flushall 删除,进行恢复。

AOF缺点:

  1. 对于同一份数据备份文件,AOF 比 RDB大
  2. AOF 开启后支持写的 QPS 会比 RDB 支持的写的 QPS 低,因为 AOF 一般会配置成每秒 fsync 操作,每秒的 fsync 操作还是很高的。
  3. 数据恢复比较慢,不适合做冷备。

参考资料:
《Redis 开发与运维》
《Redis 设计与实现》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

聊聊 Redis 高可用之持久化AOF和RDB分析 的相关文章

  • Redis 写入 .ssh/authorized_keys

    当前设置 2 个主服务器 12 个工作服务器 工作人员通过 ssh copy id 连接到主设备 主设备和工作人员正在主设备上的 redis 队列中写入数据 过去一周我遇到的问题是 Redis 正在将数据写入authorized keys
  • 使用 sidekiq 处理两个独立的 Redis 实例?

    下午好 我有两个独立但相关的应用程序 他们都应该有自己的后台队列 阅读 单独的 Sidekiq 和 Redis 进程 然而 我希望偶尔能够将工作推给app2的队列来自app1 从简单的队列 推送的角度来看 如果app1没有现有的 Sidek
  • 如何清理redis中不活跃的玩家?

    我正在制作一个使用 redis 来存储游戏状态的游戏 它可以很好地跟踪位置和玩家 但我没有一个好的方法来清理不活跃的玩家 每当玩家移动时 这是一个半慢速移动游戏 想想每秒 1 5 帧 我就会用新位置更新哈希并删除旧位置键 跟踪活跃玩家的最佳
  • Docker&Celery - 错误:Pidfile (celerybeat.pid) 已存在

    应用程序包括 姜戈 雷迪斯 芹菜 码头工人 Postgres 在将项目合并到 docker 之前 一切都运行顺利且正常 但是一旦将其移入容器 就开始出现问题 起初它开始得很好 但过了一会儿我确实收到了以下错误 celery beat 1 E
  • 如何在redis中创建自己的数据库?

    There are 0 to 15 databases in redis 我想使用 redis cli 创建自己的数据库 有什么命令可以实现吗 Redis 数据库并不等同于 MySQL 等 DBMS 中的数据库名称 这是一种为键创建隔离和命
  • Laravel - 缓存 Eloquent 并频繁更新

    是否可以对经常修改的对象使用缓存 例如 假设我们有一个 BlogPost 对象 并且有一个经常更改的 num of views 列 以及其他列 是否可以更新缓存和数据库中的 num of views 字段 而不破坏缓存对象并重新创建它 我可
  • Redis 块推送直到列表有空位

    我正在寻找类似的东西BLPUSH该命令将阻塞 直到列表的长度低于指定值max size 目的是防止生产者运行速度快于消费者时列表无限增长 功能与 python 非常相似Queue put https docs python org 3 li
  • Redis 排序集和解决关系

    我正在使用 Redis 排序集来存储我正在处理的项目的排名 我们没有预料到 我们想要如何处理关系 Redis 按字典顺序对具有相同分数的条目进行排序 但我们想要做的是对具有相同分数的所有条目给予相同的排名 例如在以下情况 redis 127
  • 从redis中检索大数据集

    一台服务器上的应用程序查询另一台服务器上运行的 Redis 查询的结果数据集约为 250kzrangebyscore objects locations inf inf这在应用程序服务器上似乎需要 40 秒 当使用命令执行时redis cl
  • SignalR 无法连接到 SSL 上的 Azure Redis

    我目前在 Azure 上托管我的 redis 缓存服务器 并让 signalR 依赖它作为骨干 使用以下内容 GlobalHost DependencyResolver UseRedis 服务器 端口 密码 eventKey 这可以在端口
  • Redis Docker compose无法处理RDB格式版本10

    我无法在 docker compose 文件中启动 redis 容器 我知道docker compose文件没问题 因为我的同事可以成功启动项目 我读到有一个删除 dump rdb 文件的解决方案 但我找不到它 我使用Windows机器 任
  • Redis发布/订阅:查看当前订阅了哪些频道

    我目前有兴趣查看我拥有的 Redis 发布 订阅应用程序中订阅了哪些频道 当客户端连接到我们的服务器时 我们将它们注册到如下所示的通道 user user id 这样做的原因是我希望能够看到谁 在线 目前 我在不知道客户端是否在线的情况下盲
  • 2 个具有共享 Redis 依赖的 Helm Chart

    目前 我有 2 个 Helm Charts Chart A 和 Chart B Chart A 和 Chart B 对 Redis 实例具有相同的依赖关系 如Chart yaml file dependencies name redis v
  • redis - 使用哈希

    我正在使用 redis 为我的 Web 应用程序实现社交流和通知系统 我是 redis 的新手 我对哈希值及其效率有一些疑问 我读过这篇很棒的文章Instagram 帖子 http instagram engineering tumblr
  • 有没有办法让特定的key在集群模式下定位到特定的redis实例上?

    我想让我的多锁位于不同的redis实例上 我发现redission可以指定一个实例来执行命令 但是如果该命令与key相关 则指定的实例会将命令传输到另一个实例 你能给我一些建议吗 你可以 但这并不是微不足道的 首先 Redis 在键中使用大
  • 如何使redis中的“HSET”子键“过期”?

    我需要使 Redis 哈希中所有超过 1 个月的密钥过期 这不可能 https github com antirez redis issues 167 issuecomment 2559040 为了保持 Redis 简单 https git
  • redis 2.8.7 Linux Sentinel环境配置问题,如何使其自启动,应该订阅什么?

    现在我们尝试使用 redis 2 8 7 作为缓存存储 来自使用 booksleeve 客户端的 NET Web 应用程序 目前看来这是一个非常有趣和令人兴奋的任务 redis 文档非常好 但由于缺乏真正的实践经验 我确实有几个关于如何正确
  • 在 Redis 上为 Django 和 Express.js 应用程序共享会话存储

    我想创建一个包含一些登录用户的 Django 应用程序 另一方面 由于我想要一些实时功能 所以我想使用 Express js 应用程序 现在的问题是 我不希望身份不明的用户访问 Express js 应用程序的日期 因此 我必须在 Expr
  • Redis+Docker+Django - 错误 111 连接被拒绝

    我正在尝试使用 Redis 作为使用 Docker Compose 的 Django 项目的 Celery 代理 我无法弄清楚我到底做错了什么 但尽管控制台日志消息告诉我 Redis 正在运行并接受连接 事实上 当我这样做时 docker
  • 如何将“.csv”数据文件导入Redis数据库

    如何将 csv 数据文件导入 Redis 数据库 csv 文件中包含 id 时间 纬度 经度 列 您能否向我建议导入 CSV 文件并能够执行空间查询的最佳方法 这是一个非常广泛的问题 因为我们不知道您想要什么数据结构 您期望什么查询等等 为

随机推荐

  • Thymeleaf URL表达式

    URL在Thymleaf中是第一类公民 xff0c 有其专有的表达式语法 64 共存在2大类URL xff1a 绝对URL http www your domain相对URL xff0c 分为四类相对于页面 user login html相
  • Maven resources的include和exclude

    Maven resources plugin支持明确声明 lt directory gt 指定的资源目录中哪些资源需要处理 xff0c 哪些资源可以不被处理 lt include gt 指明需要包括的资源 xff0c 位于src my re
  • Thymeleaf条件判断

    th if th if属性求Bool值 xff0c 只有true的时候其所在的标签及该标签中的内容才会被渲染到输出结果中 lt a href 61 34 comments html 34 th href 61 34 64 product c
  • 2019 蓝桥杯省赛 A 组模拟赛(一)C. 结果填空:马的管辖 (暴力搜索)

    题目 xff1a 在中国象棋中 xff0c 马是走日字的 一个马的管辖范围指的是当前位置以及一步之内能走到的位置 xff0c 下图的绿色旗子表示马能走到的位置 如果一匹马的某个方向被蹩马脚 xff0c 它就不能往这个方向跳了 xff0c 如
  • Ubuntu下安装使用Xfce4

    安装 xff1a 代码 sudo apt get install xfce4 xfce4 taskbar plugin xfce4 taskbar plugin是我需要 xff0c 你可不用 xff0c 完整安装xfce4 的桌面环境 su
  • isdigit()函数如何判断负数

    在使用字符序列isdigt函数时 xff0c 我们会发现它无法判断负数 xff0c 如 xff1a a 61 39 2 39 39 3 39 4 5 print a 0 isdigit print a 1 isdigit 输出 xff1a
  • 用python操作浏览器的三种方式

    第一种 xff1a selenium导入浏览器驱动 xff0c 用get方法打开浏览器 xff0c 例如 xff1a import time from selenium import webdriver def mac driver 61
  • Linux-虚拟网络设备-veth pair

    基本概念 Virtual Ethernet CableBidirectional FIFOOften used to cross namespaces Linux container 中用到一个叫做veth的东西 xff0c 这是一种新的设
  • openstack-neutron-OVS agent(持续更新)

    概述 ML2Plugin的主要工作是管理虚拟网络资源 xff0c 保证数据正确无误 xff0c 具体物理设备的设置则由Agent完成 L2Agent通常运行在Hypervisor xff0c 与neutron server通过RPC通信 x
  • VUE中使用EventSource接收服务器推送事件

    Vue项目中 xff0c EventSource触发的事件中this指向变了 使用const that 61 this xff0c 然后在EventSource触发的事件中使用that if typeof EventSource 61 61
  • VNC

    一 安装tigervnc server 二 配置登录帐号 三 生成xstartup与log日志 注意 xff1a 如果没有使用vncserver来 设置密码 xff0c 则service vncserver restart 是不会成功的 这
  • DirectUI框架GUIFW

    前言 guifw是一款基于GDI 43 的DirectUI皮肤引擎 xff0c 借鉴了DuiLib和Qt的思想 效果预览 xff1a http download csdn net detail sllins 7707771 代码已开源 xf
  • keil 提示internal command error和no sw device

    1 使用keil烧录软件的时候 xff0c jlink stlink无法识别到芯片 需要排查的问题 1 xff09 换条线 2 xff09 是不是有程序禁用了Seral Wire xff1a 使用cubeide cubeMX xff0c 容
  • 多线程编程模式之Single Threaded Execution 模式

    一 Single Threaded Execution 模式介绍 简单的来说 xff0c Single threaded execution 模式描述了在一种多线程环境下各个线程对于公用资源的使用方式 任一时刻 xff0c 只有一个线程可以
  • NVIDIA Jetson TX2 查看系统相关+运行demo

    1 查看Jetson TX2 L4T版本 xff1a head n 1 etc nv tegra release 2 查看系统版本 xff1a cat etc lsb release 3 查看系统内核 xff1a uname a 4 查看内
  • Docker镜像迁移至新的服务器(全部数据)

    1 找到你想移动的 Docker 容器的 ID 2 提交你的变更 xff0c 并且把容器保存成镜像 xff0c 命名为 newimage docker commit span class token number 3 span a09b25
  • 配置VNC环境在windows主机访问阿里云linux服务器

    配置VNC环境在windows主机访问阿里云linux服务器 虽然作为服务器使用更多的是使用字符终端连接服务器 xff0c 进行操作 xff0c 因为图形界面很消耗性能和资源 xff0c 但有的时候使用图形界面进行操作更为便捷 xff0c
  • pythondataframe输出小结

    在使用dataframe时遇到datafram在列太多的情况下总是自动换行显示的情况 xff0c 导致数据阅读困难 xff0c 效果如下 xff1a coding utf 8 import numpy as np import pandas
  • 聊聊 Redis 为什么构建自己的简单动态字符串 SDS

    我们知道 xff0c Redis 支持字符串 哈希 列表 集合和有序集合五种基本类型 那么我们如何把图片 音频 视频或者压缩文件等二进制数据保存到 Redis 中呢 xff1f 之前在使用 Memcached 缓存这类数据时是把它们转换成
  • 聊聊 Redis 高可用之持久化AOF和RDB分析

    Redis 持久化概述 Redis 是内存数据库 xff0c 数据都是存储在内存中 xff0c 为了避免进程退出导致数据的永久丢失 xff0c 需要定期将 Redis 中的数据以某种形式把内存中的数据保存到磁盘中 xff1b 当 Redis