Redis 之持久化

目录

介绍

RDB

RDB生成方式       

自动触发

手动触发 

AOF(append-only file)

Redis 4.0 混合持久化

Redis主从工作原理

总结


介绍

        Redis提供了两个持久化数据的能力,RDB Snapshot 和 AOF(Append Only FIle)日志,RDB快照能够在指定的时间间隔内生成数据快照,而AOF日志则记录了所有的写操作命令。RDB做镜像全量持久化,AOF做增量持久化。

RDB

        RDB全称Redis Database Backup file (Redis数据备份⽂件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。

        当Redis实例故障重启后,从磁盘读取快照⽂件,恢复数据。这样一来即使故障宕机,快照文件也不会丢失,数据的可靠性也就得到了保证。它会在特定的时间点对数据库的全部数据进行快照,这个快照⽂件称为 RDB文件(dump.rdb),默认是保存在当前运⾏⽬录。其中,RDB就是Redis DataBase的缩写,是一个二进制文件。

RDB生成方式       

  • 自动触发

1. 配置文件中满足默认备份配置条件(实际上执行的是bgsave命令)   

save 900 1     #900 秒之内,对数据库进行了至少 1 次修改;save 300 10    #300 秒之内,对数据库进行了至少 10 次修改save 60 10000  #60 秒之内,对数据库进行了至少 10000 次修改
# The filename where to dump the DBdbfilename dump.rdb #默认的rdb文件名dir ./    #指定rdb目录,这里代表的是当前目录

 2. 执行flushall/flushdb命令也会产生dump.rdb文件,只不过生成的是空文件,无意义

 3. 执行shutdown且没有开启 AOF 持久化也会触发 RDB 持久化

 4. 主从复制时,主节点自动触发。

  • 手动触发 

        在客户端手动执行 save/bgsave,会触发 RDB 持久化。save会阻塞当前Redis服务,不推荐使用,bgsave会fork出子线程执行 RDB 持久化,不会阻塞,推荐使用;

        执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程,导致Redis不能处理其他命令,因此线上禁止使用。

命令bgsave执行后,会立刻返回OK,Redis 会fork一个子进程,原来的redis主进程继续执行后续操作,新fork的子进程负责将数据保存到磁盘,然后退出,如下图:

        bgsave开始时会 fork主进程得到⼦进程,⼦进程共享主进程的内存数据。在Linux程序中,fork()会产生一个和父进程完全相同的子进程。 完成 fork 后读取内存数据并写⼊ RDB ⽂件。 fork 采⽤的是 copy-on-write 技术:当主 进程执⾏读操作时,访问共享内存;当主进程执⾏写操作时,则会拷⻉⼀份数据,执⾏写操作。
        每次命令执行都会将所有redis内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件。

Copy-on-write 写时复制,多更改条件下,内存被大量复制,重写完成后,数据如何释放?

本质来说,这是一个进程的内存资源管理问题:

  1. 内核提供的写时复制机制避免了大量内存拷贝以及占用过多内存
  2. 子进程fork操作只是拷贝了父进程的页表数据,当fork完成后,对于一个父进程引用的内存页,其引用值从1变成2.
  3.  当父进程进行写操作时,会申请一份新的内存页,并从原内存页copy所有数据,之后父进程就在新的内存页上进行操作。而原内存页就变成了子进程的专属内存页,引用值变成了1
  4. 子进程重写完成并退出后,内核会对资源进行回收,包括占用的内存资源
  5. 回收:对于页表来说,是进程专属,直接清理即可;而关联的内存页会将其引用值减1,如果引用值变成了0,该内存页就会被回收
  6. 也就是复制之前的页(原内存页),变成了子进程的专属页,当子进程结束后就会被回收掉

save与bgsave对比:

AOF(append-only file)

AOF全称为Append Only File(追加⽂件)。Redis处理的每⼀个写命令都会记录在AOF⽂件(读操作不记录) ,可以看做是命令⽇志⽂件。
默认情况下,redis是没有开启AOF的。开启AOF功能需要设置配置:
appendonly yes
开启 AOF 持久化后每执⾏⼀条会更改 Redis 中的数据的命令,Redis 就会将该命令写⼊到 AOF 缓冲区 server.aof_buf 中,然后再写⼊到 AOF ⽂件中 (此时在内核缓存区),最后再根据持久化⽅式( fsync策略)的配置来决定 何时将系统内核缓存区的数据同步到硬盘中的。
AOF 持久化功能的实现分为 5 步:
  1. 命令追加(append) :所有的写命令会追加到 AOF 缓冲区中。
  2. ⽂件写⼊(write) :将 AOF 缓冲区的数据写⼊到 AOF ⽂件中。这⼀步 需要调⽤write函数(系统调⽤),write将数据写⼊到了系统内核缓冲区之 后直接返回了(延迟写)。注意!此时并没有同步到磁盘。
  3. ⽂件同步(fsync) :AOF 缓冲区根据对应的持久化⽅式( fsync 策略) 向硬盘做同步操作。这⼀步需要调⽤ fsync 函数(系统调⽤), fsync 针 对单个⽂件操作,对其进⾏强制硬盘同步,fsync 将阻塞直到写⼊磁盘完 成后返回,保证了数据持久化。
  4. ⽂件重写(rewrite) :随着 AOF ⽂件越来越⼤,需要定期对 AOF ⽂件 进⾏重写,达到压缩的⽬的。
  5.  重启加载(load) :Redis 重启时,可以加载 AOF ⽂件进⾏数据恢复。
这⾥对上⾯提到的 Linux 系统调⽤再做⼀遍解释:
  • write :写⼊系统内核缓冲区之后直接返回(仅仅是写到缓冲区),不会⽴即同步到硬盘。虽然提⾼了效率,但也带来了数据丢失的⻛险。同步硬盘操作通 常依赖于系统调度机制,Linux 内核通常为 30s 同步⼀次,具体值取决于写出 的数据量和 I/O 缓冲区的状态。
  • fsync : fsync⽤于强制刷新系统内核缓冲区(同步到到磁盘),确保写磁盘操作结束才会返回。
在 Redis 的配置⽂件中存在三种不同的 AOF 持久化⽅式( fsync策略),它们分别是:
  • appendfsync always:主线程调⽤ write 执⾏写操作后,后台线程( aof_fsync 线程)⽴即会调⽤ fsync 函数同步 AOF ⽂件(刷盘),fsync 完成后线程返回,这样会严重降低 Redis 的性能

  • appendfsync everysec :主线程调⽤ write 执⾏写操作后⽴即返回,由后台线程( aof_fsync 线程)每秒钟调⽤ fsync 函数(系统调⽤)同步⼀次 AOF ⽂件

  • appendfsync no :主线程调⽤ write 执⾏写操作后⽴即返回,让操作系统决定何时进⾏同步,Linux 下⼀般为 30 秒⼀次

推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。

AOF重写

AOF文件里可能有太多没用指令,所以AOF会定期根据内存的最新数据生成aof文件

如下两个配置可以控制AOF自动重写频率

auto‐aof‐rewrite‐min‐size 64mb //aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大
auto‐aof‐rewrite‐percentage 100 //aof文件自上一次重写后文件大小增长了100%则再次触发重写

当然AOF还可以手动重写,进入redis客户端执行命令bgrewriteaof重写AOF

        由于 AOF 重写会进⾏⼤量的写⼊操作,为了避免对 Redis 正常处理命令请求造成影响,Redis 将 AOF 重写程序放到⼦进程⾥执⾏。

        图 1 展示的是 AOFRW 的实现原理。当 AOFRW 被触发执行时,Redis 首先会 fork 一个子进程进行后台重写操作,该操作会将执行 fork 那一刻 Redis 的数据快照全部重写到一个名为 temp-rewriteaof-bg-pid.aof 的临时 AOF 文件中。
        由于重写操作为子进程后台执行,主进程在 AOF 重写期间依然可以正常响应用户命令。因此,为了让子进程最终也能获取重写期间主进程产生的增量变化,主进程除了会将执行的写命令写入 aof_buf,还会写一份到 aof_rewrite_buf 中进行缓存。在子进程重写的后期阶段,主进程会将 aof_rewrite_buf 中累积的数据使用 pipe 发送给子进程,子进程会将这些数据追加到临时 AOF 文件中(详细原理可参考[1])。
        当主进程承接了较大的写入流量时,aof_rewrite_buf 中可能会堆积非常多的数据,导致在重写期间子进程无法将 aof_rewrite_buf 中的数据全部消费完。此时,aof_rewrite_buf 剩余的数据将在重写结束时由主进程进行处理。

        当子进程完成重写操作并退出后,主进程会在 backgroundRewriteDoneHandler中处理后续的事情。首先,将重写期间 aof_rewrite_buf 中未消费完的数据追加到临时 AOF 文件中。其次,当一切准备就绪时,Redis 会使用 rename 操作将临时AOF 文件原子的重命名为 server.aof_filename,此时原来的 AOF 文件会被覆盖。至此,整个 AOFRW 流程结束。

图1:

AOF重写存在的问题

  • 内存开销

        由图 1 可以看到,在 AOFRW 期间,主进程会将 fork 之后的数据变化写进aof_rewrite_buf 中,aof_rewrite_buf 和 aof_buf 中的内容绝大部分都是重复的,因此这将带来额外的内存冗余开销。

  • CPU 开销

        CPU 的开销主要有三个地方,分别解释如下:
                1) 在 AOFRW 期间,主进程需要花费 CPU 时间向 aof_rewrite_buf 写数据,并使用                      eventloop 事件循环向子进程发送 aof_rewrite_buf 中的数据。

                2) 在子进程执行重写操作的后期,会循环读取 pipe 中主进程发送来的增量数据,
                    然后追加写入到临时 AOF 文件。

                3) 在子进程完成重写操作后,主进程会在 backgroundRewriteDoneHandler 中进
                    行收尾工作。其中一个任务就是将在重写期间 aof_rewrite_buf 中没有消费完
                    成的数据写入临时 AOF 文件。如果 aof_rewrite_buf 中遗留的数据很多,这里
                    也将消耗 CPU 时间。

  • 磁盘 IO 开销

        如前文所述,在 AOFRW 期间,主进程除了会将执行过的写命令写到 aof_buf 之外,
还会写一份到 aof_rewrite_buf 中。aof_buf 中的数据最终会被写入到当前使用的旧 AOF 文件中,产生磁盘 IO。同时,aof_rewrite_buf 中的数据也会被写入重写生成的新 AOF 文件中,产生磁盘 IO。因此,同一份数据会产生两次磁盘 IO。

阿⾥ Redis 在最初也遇到了这些AOF的重写问题,在内部经过多次迭代开发,实现了Multi-part AOF 机制来解决,同时也贡献给了社区并随此次 7.0 发布。具体⽅法是采⽤ base(全量数据)+incr(增量数据)独⽴⽂件存储的⽅式。

Multi-part AOF
顾名思义,MP-AOF 就是将原来的单个 AOF 文件拆分成多个 AOF 文件。在 MP-AOF中,我们将 AOF 分为三种类型,分别为:
  • BASE:表示基础 AOF,它一般由子进程通过重写产生,该文件最多只有一个;
  • INCR:表示增量 AOF,它一般会在 AOFRW 开始执行时被创建,该文件可能存在多个;
  • HISTORY:表示历史 AOF,它由 BASE 和 INCR AOF 变化而来,每次 AOFRW 成功完成时,本次 AOFRW 之前对应的 BASE 和 INCR AOF 都将变为 HISTORY,HISTORY 类型的 AOF 会被 Redis 自动删除。
为了管理这些 AOF 文件,我们引入了一个 manifest(清单)文件来跟踪、管理这些AOF。同时,为了便于 AOF 备份和拷贝,我们将所有的 AOF 文件和 manifest 文件放入一个单独的文件目录中,目录名由 appenddirname 配置(Redis7.0 新增配置项)决定。

         图 2 展示的是在 MP-AOF 中执行一次 AOFRW 的大致流程。在开始时我们依然会fork 一个子进程进行重写操作,在主进程中,我们会同时打开一个新的 INCR 类型的 AOF 文件,在子进程重写操作期间,所有的数据变化都会被写入到这个新打开的INCR AOF 中。子进程的重写操作完全是独立的,重写期间不会与主进程进行任何的数据和控制交互,最终重写操作会产生一个 BASE AOF。新生成的 BASE AOF 和新打开的 INCR AOF 就代表了当前时刻 Redis 的全部数据。AOFRW 结束时,主进程会负责更新 manifest 文件,将新生成的 BASE AOF 和 INCR AOF 信息加入进去,并将之前的 BASE AOF 和 INCR AOF 标记为 HISTORY(这些 HISTORY AOF 会被 Redis 异步删除)。一旦 manifest 文件更新完毕,就标志整个 AOFRW 流程结束。
        由图 2 可以看到,我们在 AOFRW 期间不再需要 aof_rewrite_buf,因此去掉了对应的内存消耗。同时,主进程和子进程之间也不再有数据传输和控制交互,因此对应的 CPU 开销也全部去掉。

Redis 4.0 混合持久化

重启 Redis 时,我们很少使用 RDB来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 RDB来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。 Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。
通过如下配置可以开启混合持久化(必须先开启aof):

# aof‐use‐rdb‐preamble yes

 如果开启了混合持久化,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。
于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可以完全替代之前的AOF 全量文件重放,因此重启效率大幅得到提升。

混合持久化AOF文件结构如下

Redis主从工作原理

Redis主从架构

Redis主从工作原理

如果你为master配置了一个slave,不管这个slave是否是第一次连接上Master,它都会发送一个PSYNC命令给master请求复制数据。

master收到PSYNC命令后,会在后台进行数据持久化通过bgsave生成最新的rdb快照文件,持久化期间,master会继续接收客户端的请求,它会把这些可能修改数据集的请求缓存在内存中。当持久化进行完毕以后,master会把这份rdb文件数据集发送给slave,slave会把接收到的数据进行持久化生成rdb,然后再加载到内存中。然后,master再将之前缓存在内存中的命令发送给slave。

当master与slave之间的连接由于某些原因而断开时,slave能够自动重连Master,如果master收到了多个slave并发连接请求,它只会进行一次持久化,而不是一个连接一次,然后再把这一份持久化的数据发送给多个并发连接的slave。

主从复制(全量复制)流程图:

数据部分复制

当master和slave断开重连后,一般都会对整份数据进行复制。但从redis2.8版本开始,redis改用可以支持部分数据复制的命令PSYNC去master同步数据,slave与master能够在网络连接断开重连后只进行部分数据复制(断点续传)。

master会在其内存中创建一个复制数据用的缓存队列,缓存最近一段时间的数据,master和它所有的slave都维护了复制的数据下标offset和master的进程id,因此,当网络连接断开后,slave会请求master继续进行未完成的复制,从所记录的数据下标开始。如果master进程id变化了,或者从节点数据下标offset太旧,已经不在master的缓存队列里了,那么将会进行一次全量数据的复制。

主从复制(部分复制,断点续传)流程图:

如果有很多从节点,为了缓解主从复制风暴(多个从节点同时复制主节点导致主节点压力过大),可以做如下架构,让部分从节点与从节点(与主节点同步)同步数据

总结

RDB和AOF对比

持久化可以保证数据安全,但会带来额外的开销,请遵循下列建议:
• ⽤来做缓存的Redis实例尽量不要开启持久化功能
• 建议关闭RDB持久化功能,使⽤AOF持久化
• 利⽤脚本定期在slave节点做RDB,实现数据备份
• 设置合理的rewrite阈值,避免频繁的bgrewrite
• 配置no-appendfsync-on-rewrite =yes,禁⽌在rewrite期间做aof,避免因AOF引起的阻塞
部署建议:
• Redis实例的物理机要预留⾜够内存,应对fork和rewrite
• 单个Redis实例内存上限不要太⼤,例如8G。可以加快fork的速度、减少主从同步、数据迁移压⼒
• 不要与CPU密集型应⽤部署在⼀起
• 不要与⾼硬盘负载应⽤⼀起部署。例如:数据库、消息队列

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/928453.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux内核4.14版本——ccf时钟子系统(3)——ccf一些核心结构体

目录 1. struct clk_hw 2. struct clk_ops 3. struct clk_core 4. struct clk_notifier 5. struct clk 6. struct clk_gate 7. struct clk_divider 8. struct clk_mux 9. struct clk_fixed_factor 10. struct clk_fractional_divider 11. struct clk_multiplier 12…

【JavaEE初阶 — 网络编程】实现基于TCP协议的Echo服务

TCP流套接字编程 1. TCP & UDP 的区别 TCP 的核心特点是面向字节流,读写数据的基本单位是字节 byte 2 API介绍 2.1 ServerSocket 定义 ServerSocket 是创建 TCP 服务端 Socket 的API。 构造方法 方法签名 方法说明 ServerS…

开发者如何使用GCC提升开发效率GUI操作

看此篇前请先阅读https://blog.csdn.net/qq_20330595/article/details/144139026?spm1001.2014.3001.5502 先上效果图 找到对应的环境版本 配置环境 目录结构 CtrlShiftP c_cpp_properties.json {"configurations": [{"name": "Win32","i…

高速定向广播声光预警系统赋能高速安全管控

近年来,高速重大交通事故屡见不鲜,安全管控一直是高速运营的重中之重。如何利用现代化技术和信息化手段,创新、智能、高效的压降交通事故的发生概率,优化交通安全管控质量,是近年来交管部门的主要工作,也是…

BiGRU:双向门控循环单元在序列处理中的深度探索

一、引言 在当今的人工智能领域,序列数据的处理是一个极为重要的任务,涵盖了自然语言处理、语音识别、时间序列分析等多个关键领域。循环神经网络(RNN)及其衍生结构在处理序列数据方面发挥了重要作用。然而,传统的 RN…

shell编程7,bash解释器的 for循环+while循环

声明! 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&#…

AI开发:生成式对抗网络入门 模型训练和图像生成 -Python 机器学习

阶段1:GAN是个啥? 生成式对抗网络(Generative Adversarial Networks, GAN),名字听着就有点“对抗”的意思,没错!它其实是两个神经网络互相斗智斗勇的游戏: 生成器(Gene…

HarmonyOS开发中,如何高效定位并分析内存泄露相关问题

HarmonyOS开发中,如何高效定位并分析内存泄露相关问题 (1)Allocation的应用调试方式Memory泳道Native Allocation泳道 (2)Snapshot(3)ASan的应用使用约束配置参数使能ASan方式一方式二 启用ASanASan检测异常码 (4)HWASan的应用功能介绍约束条件使能HWASan方式一方式…

【Python】Selenium模拟在输入框里,一个字一个字地输入文字

我们平常在使用Selenium模拟键盘输入内容,常用的是用send_keys来在输入框上输入字: 基本的输入方式: input_element driver.find_element(By.ID, searchBox) input_element.send_keys("我也爱你") #给骚骚的自己发个骚话不过这种…

泷羽sec学习打卡-shell命令6

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都 与本人无关,切莫逾越法律红线,否则后果自负 关于shell的那些事儿-shell6 if条件判断for循环-1for循环-2实践是检验真理的唯一标准 if条件判断 创建…

【ArkTS】使用AVRecorder录制音频 --内附录音机开发详细代码

系列文章目录 【ArkTS】关于ForEach的第三个参数键值 【ArkTS】“一篇带你读懂ForEach和LazyForEach” 【小白拓展】 【ArkTS】“一篇带你掌握TaskPool与Worker两种多线程并发方案” 【ArkTS】 一篇带你掌握“语音转文字技术” --内附详细代码 【ArkTS】技能提高–“用户授权”…

数据分析案例-笔记本电脑价格数据可视化分析

🤵‍♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞&#x1f4…

系统监控——分布式链路追踪系统

摘要 本文深入探讨了分布式链路追踪系统的必要性与实施细节。随着软件架构的复杂化,传统的日志分析方法已不足以应对问题定位的需求。文章首先解释了链路追踪的基本概念,如Trace和Span,并讨论了其基本原理。接着,文章介绍了SkyWa…

游戏引擎学习第25天

Git: https://gitee.com/mrxiao_com/2d_game 今天的计划 总结和复述: 这段时间的工作已经接近尾声,虽然每次编程的时间只有一个小时,但每一天的进展都带来不少收获。尽管看起来似乎花费了很多时间,实际上这些日积月累的时间并未…

GaussDB TPOPS 搭建流程记录

目录 前言 环境准备 安装前准备 安装TPOPS 总结 前言 由于工作需要,准备将现有Oracle数据切换至GaussDB数据库。在这里记录一下安装GaussDB数据库过程踩的坑。 首先,我装的是线下版本,需要先装一个GaussDB轻量化管理平台(…

Web网页设计作业成品源码分享(持续更新)

🎉Web前端大作业专栏推荐 📚Web前端期末大作业源码分享 ✍️html网页设计、web前后端网站制作、大学生网页设计作业、个人网站制作、jQuery网站设计、uniapp小程序、vue网站设计、node.js网站设计、网页成品模板、期末大作业,各种设计应有尽有…

facebook欧洲户开户条件有哪些又有何优势?

在当今数字营销时代,Facebook广告已成为企业推广产品和服务的重要渠道。而为了更好地利用这一平台,广告主们需要理解不同类型的Facebook广告账户。Facebook广告账户根据其属性可分为多种类型,包括个人广告账户、企业管理(BM&#…

Qt 2D绘图之三:绘制文字、路径、图像、复合模式

参考文章链接: Qt 2D绘图之三:绘制文字、路径、图像、复合模式 绘制文字 除了绘制图形以外,还可以使用QPainter::darwText()函数来绘制文字,也可以使用QPainter::setFont()设置文字所使用的字体,使用QPainter::fontInfo()函数可以获取字体的信息,它返回QFontInfo类对象…

一种多功能调试工具设计方案开源

一种多功能调试工具设计方案开源 设计初衷设计方案具体实现HUB芯片采用沁恒微CH339W。TF卡功能网口功能SPI功能IIC功能JTAG功能下行USB接口 安路FPGA烧录器功能Xilinx FPGA烧录器功能Jlink OB功能串口功能RS232串口RS485和RS422串口自适应接口 CAN功能烧录器功能 目前进度后续计…

【C++】深入优化计算题目分析与实现

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯第一题:圆的计算我的代码实现代码分析改进建议改进代码 老师的代码实现代码分析可以改进的地方改进代码 💯第二题:对齐输出我的代码实现…