聊聊 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分析 的相关文章

  • 如何将node.js管道传输到redis?

    我有很多数据要插入 SET INCR 到redis DB 所以我正在寻找pipeline http redis io topics pipelining 质量插入 http redis io topics mass insert通过node
  • 如何设置和获取Redis中存储的对象?

    我试图在 redis 中存储一个对象 当我获取该对象时 它似乎不起作用 I tried u User new u name blankman redis set test u x redis get test x name error 我想
  • 使用 Sentinels 升级 Redis 的最佳实践?

    我有 3 个 Redis 节点 由 3 个哨兵监视 我进行了搜索 文档似乎不清楚如何最好地升级此类配置 我目前使用的是 3 0 6 版本 我想升级到最新的 5 0 5 我对这方面的程序有几个疑问 升级两个大版本可以吗 我在我们的暂存环境中执
  • 使用 Celery 通过 Gevent 进行实时、同步的外部 API 查询

    我正在开发一个 Web 应用程序 该应用程序将接收用户的请求 并且必须调用许多外部 API 来编写对该请求的答案 这可以直接从主 Web 线程使用 gevent 之类的东西来扇出请求来完成 或者 我在想 我可以将传入的请求放入队列中 并使用
  • 为什么Redis中没有有序的hashmap?

    Redis 数据类型 http redis io topics data types包括排序集 http redis io topics data types intro sorted sets以及其他用于键值存储的必要数据结构 但我想知道
  • 在 Redis 上为 Django 和 Express.js 应用程序共享会话存储

    我想创建一个包含一些登录用户的 Django 应用程序 另一方面 由于我想要一些实时功能 所以我想使用 Express js 应用程序 现在的问题是 我不希望身份不明的用户访问 Express js 应用程序的日期 因此 我必须在 Expr
  • 如何在Redis中只保存一个数据库?

    我是 Redis 新手 有一个与备份相关的问题 目前 我有一个实例在 Windows 服务器上运行 在这个实例中 我当前有一项 工作 将数据存储在一个数据库中 我不想备份这些数据 我必须创造一份新工作 我的第一个想法是将数据存储在另一个数据
  • Spring Redis删除不删除key

    我正在尝试删除一个 Redis 键 但由于某种原因它没有删除 但也没有抛出异常 这是我要删除的代码 import com example service CustomerService import com example model Cu
  • 如何延长 django-redis 中的缓存 ttl(生存时间)?

    我正在使用 django 1 5 4 和 django redis 3 7 1 我想延长缓存的 ttl 生存时间 当我取回它时 这是示例代码 from django core cache import cache foo cache get
  • Laravel Redis 配置

    我目前正在使用 Laravel 和 Redis 创建一个应用程序 几乎一切都工作正常 我按照文档中的说明扩展了身份验证 用户可以订阅 登录 注销 我可以创建内容 所有内容都存储在 Redis 中 但我有一个问题 我无法运行 php arti
  • 将文件传递给活动作业/后台作业

    我通过标准文件输入接收请求参数中的文件 def create file params file upload Upload create file file filename img png end 但是 对于大型上传 我想在后台作业中执行
  • 为什么单个 Redis 实例不是线程安全的?

    https github com xetorthio jedis wiki Getting started https github com xetorthio jedis wiki Getting started 在多线程环境中使用Jed
  • 没有适用于机器人的 Laravel 会话

    我在大型 Laravel 项目和 Redis 存储方面遇到问题 我们将会话存储在 Redis 中 我们已经有 28GB 的 RAM 然而 它的运行速度仍然相对较快 达到了极限 因为我们有来自搜索引擎机器人的大量点击 每天超过 250 000
  • Web API 缓存 - 如何使用分布式缓存实现失效

    我有一个 API 目前不使用任何缓存 我确实有一个正在使用的中间件 它可以生成缓存标头 Cache Control Expires ETag Last Modified 使用https github com KevinDockx HttpC
  • 集合成员的 TTL

    Redis 是否可以不为特定键而是为集合的成员设置 TTL 生存时间 我正在使用 Redis 文档提出的标签结构 数据是简单的键值对 标签是包含与每个标签对应的键的集合 例如 gt SETEX id id 1 100 Lorem ipsum
  • 使用 Redis 中的键

    我是 Redis 和键值数据库的新手 你能告诉我如何在redis中正确实现这种关系方法吗 我有一个关系表 其中两个键对应一个值 master id slave id 价值 Example 主站 ID 从属ID 价值 1 1 值1 2 1 值
  • ServiceStack PooledRedisClientManager 故障转移如何工作?

    根据 git commit 消息 ServiceStack 最近添加了故障转移支持 我最初认为这意味着我可以关闭我的一个 Redis 实例 并且我的池客户端管理器将优雅地处理故障转移并尝试与我的备用 Redis 实例之一连接 不幸的是 我的
  • Redis 是否使用用户名进行身份验证?

    我已经在我的环境中设置了Redis 并且只看到了通过密码授权的部分 有没有办法也设置用户名 还是只能通过密码验证 Redis 6 上有 ACL 这些都有一个用户名 查看https redis io topics acl https redi
  • 检查 Redis 列表中是否已存在某个值

    我想知道是否有办法检查 redis 列表中是否已存在某个键 我无法使用集合 因为我不想强制唯一性 但我确实希望能够检查字符串是否确实存在 Thanks 您的选择如下 Using LREM如果发现则更换它 维护一个单独的SET与您的LIST
  • 在 Rails 应用程序上将 HASH 保存到 Redis

    我刚刚开始使用 Redis 和 Rails 所以这可能是一个愚蠢的问题 我试图将哈希值保存到 Redis 服务器 但是当我检索它时 它只是一个字符串 IE hash field gt value field2 gt value2 redis

随机推荐

  • 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