【Redis技术进阶之路】「原理分析系列开篇」探索事件驱动枚型与数据特久化原理实现(数据持久化的实现RDB)

揭秘高效存储模型与数据结构底层实现

  • 【专栏简介】
    • 【技术大纲】
    • 【专栏目标】
    • 【目标人群】
      • 1. Redis爱好者与社区成员
      • 2. 后端开发和系统架构师
      • 3. 计算机专业的本科生及研究生
  • Redis数据持久化的必要
  • Redis数据持久化的实现
    • RDB的持久化机制
      • RDB文件的创建与载入
        • SAVE
        • BGSAVE
          • SAVE与BGSAVE命令在逻辑层面的主要差异
        • Redis服务器在启动时打印
        • 设置保存条件
          • saveparams
        • 检查保存条件是否满足
    • AOF的持久化机制
  • 最终总结
    • SAVE命令执行时的服务器状态
    • BGSAVE命令执行时的服务器状态
    • RDB文件载入时的服务器状态

【专栏简介】

随着数据需求的迅猛增长,持久化和数据查询技术的重要性日益凸显。关系型数据库已不再是唯一选择,数据的处理方式正变得日益多样化。在众多新兴的解决方案与工具中,Redis凭借其独特的优势脱颖而出。

【技术大纲】

为何Redis备受瞩目?原因在于其学习曲线平缓,短时间内便能对Redis有初步了解。同时,Redis在处理特定问题时展现出卓越的通用性,专注于其擅长的领域。深入了解Redis后,您将能够明确哪些任务适合由Redis承担,哪些则不适宜。这一经验对开发人员来说是一笔宝贵的财富。
在这里插入图片描述

在这个专栏中,我们将专注于Redis的6.2版本进行深入分析和介绍。Redis 6.2不仅是我个人特别偏爱的一个版本,而且在实际应用中也被广泛认为是稳定性和性能表现都相当出色的版本

【专栏目标】

本专栏深入浅出地传授Redis的基础知识,旨在助力读者掌握其核心概念与技能。深入剖析了Redis的大多数功能以及全部多机功能的实现原理,详细展示了这些功能的核心数据结构和关键算法思想。读者将能够快速且有效地理解Redis的内部构造和运作机制,这些知识将助力读者更好地运用Redis,提升其使用效率。

将聚焦于Redis的五大数据结构,深入剖析各种数据建模方法,并分享关键的管理细节与调试技巧。

【目标人群】

Redis技术进阶之路专栏:目标人群与受众对象,对于希望深入了解Redis实现原理底层细节的人群

1. Redis爱好者与社区成员

Redis技术有浓厚兴趣,经常参与社区讨论,希望深入研究Redis内部机制、性能优化和扩展性的读者。

2. 后端开发和系统架构师

在日常工作中经常使用Redis作为数据存储和缓存工具,他们在项目中需要利用Redis进行数据存储、缓存、消息队列等操作时,此专栏将为他们提供有力的技术支撑。

3. 计算机专业的本科生及研究生

对于学习计算机科学、软件工程、数据分析等相关专业的在校学生,以及对Redis技术感兴趣的教育工作者,此专栏可以作为他们的学习资料和教学参考。

无论是初学者还是资深专家,无论是从业者还是学生,只要对Redis技术感兴趣并希望深入了解其原理和实践,都是此专栏的目标人群和受众对象

让我们携手踏上学习Redis的旅程,探索其无尽的可能性!


Redis数据持久化的必要

鉴于Redis是一种基于内存的数据库系统,其核心优势在于将数据库状态直接存储在高速的内存空间之中。然而,这种设计也带来了一项重要考量:若未采取适当的持久化措施,将内存中的数据库状态安全地转移到磁盘上,那么随着服务器进程的终止,这些宝贵的数据将面临丢失的风险,因为内存中的数据不具备持久性,断电或进程退出均会导致其消失无踪。因此,为确保数据的安全与连续性,Redis提供了多种持久化机制,以在服务器运行之外保护并恢复数据库状态,避免数据的意外丢失。

Redis数据持久化的实现

Redis作为一种高效的键值对存储系统,其核心功能在于提供一个灵活且强大的数据库服务器环境。此服务器架构内,通常承载着若干个非空的数据库实例,每个实例均具备独立的空间以容纳任意数量的键值对数据。
为了简化理解与操作,我们习惯上将Redis服务器中所有非空数据库及其内部存储的键值对集合,统一抽象并称之为“数据库状态”。
在这里插入图片描述
为了更直观地说明,请考虑上面的图所呈现的示例,它生动地展示了一个Redis服务器的内部构造,其中包含了三个非空的数据库实例。这三个数据库,连同它们各自存储的丰富键值对集合,共同构成了该Redis服务器在某一时刻的完整数据库状态。

为了有效应对内存数据易失性的问题,Redis巧妙地引入了RDB(Redis Database)持久化功能。这一创新机制能够定时或按需将Redis内存中的完整数据库状态快照保存至磁盘之上,从而构建起一道坚实的防线,防止数据因系统崩溃、意外断电等突发状况而遭受不可挽回的损失。

RDB的持久化机制

RDB持久化机制在Redis中展现了高度的灵活性与可配置性,它既可以响应管理员的手动触发,根据即时需求执行数据快照;也可以根据服务器的预设配置,实现定时自动备份,确保数据更新的连续记录。这一过程将选定时间点的数据库状态精准捕捉,并封装成一个紧凑的、经过优化的二进制RDB文件(如下图所示),有效减少了存储空间占用,同时保证了数据的完整性与一致性。
在这里插入图片描述
尤为重要的是,这个RDB文件不仅是数据的简单集合,更是Redis数据库状态在某一历史时刻的忠实记录。在需要时,通过加载这一文件,可以迅速且准确地还原至生成RDB文件时的数据库状态(如下图所示),为数据的恢复与迁移提供了强有力的支持。
在这里插入图片描述
由于RDB文件被妥善保存在硬盘上,这一特性确保了即便Redis服务器进程意外终止,乃至承载Redis服务器的计算机遭遇停机情况,只要RDB文件保持完好无损,Redis服务器便能够凭借该文件恢复数据库的先前状态,从而保障数据的持久性和可恢复性。

RDB文件的创建与载入

Redis提供了两个关键的命令来生成RDB文件,以实现数据的持久化。其中,SAVE命令是一个同步操作,它会立即阻塞Redis服务器,直到RDB文件被完整创建并保存到硬盘上为止。而BGSAVE命令则采用异步方式执行,它会在后台启动一个子进程来负责生成RDB文件,从而避免了阻塞主服务器进程,确保了Redis服务的连续性和响应性。这两个命令共同为Redis用户提供了灵活的数据备份和恢复选项。

SAVE

当执行SAVE命令时,Redis服务器进程会进入阻塞状态,这意味着在RDB文件被完整创建并安全保存到硬盘之前,服务器将暂停处理任何来自客户端的命令请求。此期间,所有试图与Redis服务器交互的操作都将被挂起,直至SAVE命令完成,服务器恢复正常运行状态。

//等待直到RDB文件创建完半
redis>SAVE
OK

因此,虽然SAVE命令能够确保数据的即时持久化,但在高并发或需要低延迟响应的场景中,其阻塞特性可能需要谨慎使用。

BGSAVE

和SAVE命令直接阻塞服务器进程的做法不同,BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求。

//派生子进程,并由子进程创建RDB文件
redis>BGSAVE
Background saving started

RDB文件的创建过程实际上是由rdb.c源文件中的rdbSave函数精心实现的。SAVE命令与BGSAVE命令虽共享这一核心功能,却以截然不同的方式调用rdbSave函数,它们之间的差异通过以下简化的伪代码清晰展现:

function SAVE():  # 直接在主线程中调用 rdbSave 函数,这会阻塞服务器直到文件生成完成  rdbSave()  # 阻塞期间,服务器无法处理任何命令请求  function BGSAVE():  # 在后台启动一个新进程来执行 rdbSave 函数  # 这样做不会阻塞主服务器进程,允许其继续处理命令请求  forkChildProcess()  if (inChildProcess()):  rdbSave()  exitChildProcess()  

主服务器进程在执行BGSAVE命令时,能够继续其正常的操作流程,完全不受rdbSave操作的影响,这一特性通过创建独立的子进程来实现异步处理。

SAVE与BGSAVE命令在逻辑层面的主要差异
  • 处理的阻塞模式以及线程执行的区别SAVE命令采取的是直接在主线程中同步执行的方式,这不可避免地会导致服务器在处理RDB文件生成期间无法响应其他命令请求,从而造成阻塞;而BGSAVE命令则巧妙地绕过了这一问题,它通过创建一个新的子进程来异步执行rdbSave操作,这样主服务器进程就能够持续运行,不受任何干扰,确保了Redis服务的高可用性和低延迟响应。
  • 使用SAVE命令或者BGSAVE命令创建RDB文件不同:与创建RDB文件的操作(通过SAVEBGSAVE命令)不同,RDB文件的加载过程是自动且无需用户干预的。当Redis服务器启动时,它会检查是否存在RDB文件。一旦检测到RDB文件的存在,Redis服务器将自动启动加载流程,无需任何专门的加载命令。
Redis服务器在启动时打印

Redis服务器在启动时生成的一系列日志记录中的一个关键条目。特别地,当日志中出现“DB loaded from disk: …”这一条目时,它标志着Redis服务器已经成功地自动从磁盘上加载了RDB文件。

[1279]17 Aug 0:07:01.270 Server started,Redis version 2.9.11
[1279]17 Aug 0:07:01.289 DB loaded from disk:0.018 seconds
[1279]17 Aug 0:07:01.289 The server is now ready to accept connections on port
6379

这一操作通常在服务器启动过程的早期阶段进行,确保了Redis数据库能够迅速恢复到最近一次持久化保存的状态,为用户和应用程序提供了无缝的数据访问体验。
在这里插入图片描述
具体到RDB文件的加载过程,rdb.c/rdbLoad函数扮演着核心角色,它负责从RDB文件中读取并恢复数据库的所有数据。这个函数与用于创建RDB文件的rdbSave函数在功能上相互对应,它们之间的关系可以通过上图直观展现,共同构成了Redis数据持久化与恢复机制的重要组成部分。

设置保存条件

在Redis服务器启动之际,用户拥有高度的灵活性来配置其行为,特别是通过明确指定配置文件或直接在启动命令中传入参数的方式来设置save选项。这一机制赋予了用户精确控制数据持久化策略的能力。

save 900 1
save 300 10
save 60 10000

若用户选择不显式设置save选项,Redis服务器则会采取一种预设的、保守的策略来自动配置这些条件,确保数据在特定条件下能够自动保存到磁盘上,以此作为默认的数据持久化保障措施。

struct redisserver{//记录了保存条件的数组struct saveparam saveparams;
};
saveparams

saveparams属性被精心设计为一个数组结构,其中每一个元素都承载着saveparam结构体的精髓。这些saveparam结构体各自独立,扮演着至关重要的角色,它们分别存储了关于save选项配置的具体保存条件。

struct saveparam{/1秒数timet seconds;//修改数int changes;
};

在Redis服务器的状态结构中,saveparams数组将以一种直观且结构化的方式呈现,其布局大致对应于下图所示。该图不仅清晰地展示了saveparams数组的组织形式,还详细描绘了数组中每个saveparam元素的具体内容,包括它们各自代表的数据保存条件。
在这里插入图片描述

检查保存条件是否满足

Redis的服务器内部设有一个周期性的操作函数serverCron,它默认以每100毫秒为周期自动执行,肩负起维护服务器稳定运行的重任。在serverCron的众多职责之中,尤为关键的一项便是定期审查save选项所预设的数据保存条件。一旦这些条件得以满足,serverCron便会立即触发BGSAVE命令,以一种非阻塞的方式启动数据的后台保存流程。

以下伪代码展示了serverCron函数检查保存条件的过程,服务器周期性操作函数,默认每隔一定时间执行一次,用于维护服务器状态

def servercron():  # 遍历所有保存条件  for saveparam in server.saveparams:  # 计算距离上次执行保存操作的时间(秒)  elapsed_time = time.time() - server.lastsave  # 如果数据库状态的修改次数超过条件所设置的次数  # 并且距离上次保存的时间超过条件所设置的时间  if server.dirty > saveparam.changes and elapsed_time >= saveparam.seconds:  # 执行BGSAVE命令,以非阻塞方式保存数据库  BGSAVE()  # (可选)更新lastsave时间和其他可能的维护操作  server.lastsave = time.time()  # 这里可以添加更多维护操作的代码  

程序会全面遍历saveparams数组,细致地检查数组中的每一个保存条件。一旦发现有任意一个条件得到了满足,程序便会迅速响应,指示服务器执行BGSAVE命令。

AOF的持久化机制

当Redis服务器启用了AOF持久化功能时,为了确保数据的完整性和最新性,服务器会优先选择AOF文件来恢复数据库状态。
反之,仅当AOF持久化功能被禁用或AOF文件不可用(例如,文件损坏)时,服务器才会回退到使用RDB文件来进行数据库状态的还原。

服务器决定采用何种文件来恢复数据库状态的这一逻辑流程,如下图所示,它通过智能判断,确保了在各种情况下都能以最合适的方式恢复数据。
在这里插入图片描述

最终总结

SAVE命令执行时的服务器状态

SAVE命令被触发执行时,Redis服务器会进入一种阻塞状态,这意味着在此期间,服务器将暂停处理任何来自客户端的新命令请求。因此,若SAVE命令正处于执行过程中,所有尝试与服务器通信的客户端所发送的命令请求都将遭遇拒绝,服务器不会对这些请求进行任何处理或响应。

直至SAVE命令圆满完成其数据持久化任务,并重新开放对外部命令的接收窗口后,Redis服务器才会开始逐一处理并响应那些之前因阻塞而积压的客户端命令请求。这一过程确保了数据的一致性和完整性,尽管在某些情况下可能会暂时影响服务的响应性。

BGSAVE命令执行时的服务器状态

由于BGSAVE命令巧妙地利用了子进程来执行数据的保存工作,Redis服务器在子进程忙于创建RDB文件的同时,能够维持其正常的运行状态,继续高效地处理来自客户端的命令请求。然而,值得注意的是,在BGSAVE命令的执行周期内,服务器对于SAVEBGSAVE以及BGREWRITEAOF这三个特定命令的处理逻辑会经历一些调整,以确保数据的一致性和操作的合理性。

具体而言,当BGSAVE正在运行时,如果客户端尝试执行SAVE命令(该命令会阻塞服务器直到完成全量快照),Redis服务器可能会拒绝执行该命令,或者返回一个指示当前已有后台保存任务在运行的响应,以避免不必要的资源竞争和潜在的性能瓶颈。对于BGSAVE命令本身的重复请求,服务器可能会选择忽略或返回一个提示信息,指出一个后台保存任务已经在进行中,无需重复启动。

RDB文件载入时的服务器状态

在服务器载入RDB文件的过程中,它会全神贯注地投入到这一关键任务中,期间将维持阻塞状态,以确保载入工作的顺利进行和数据的完整性。这种阻塞状态将持续到RDB文件被完全载入并恢复为内存中的数据结构为止,之后服务器才会重新恢复对外部请求的响应,继续提供其丰富的服务功能。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot+Vue的社区智慧消防管理系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏:Java精选实战项目…

Python数据分析和可视化详解

Python数据分析和可视化详解 Python 是当前最受欢迎的数据分析和可视化工具之一。凭借其简单的语法和强大的第三方库,Python 为数据科学家、分析师和工程师提供了广泛的工具,用于处理、分析和展示数据。本文将介绍如何使用 Python 进行数据分析与可视化…

8.12 矢量图层面要素单一符号使用五(点符号填充)

8.12 矢量图层面要素单一符号使用五(点符号填充)_mapguide edit composite symbolization 使符号填充面-CSDN博客 目录 前言 点符号填充(Point pattern fill) QGis设置面符号为点符号填充(Point pattern fill) 二次开发代码实…

数学建模-线性规划讲解(Matlab版本)

引言 相信不少小伙伴刚开始接触数学建模时,第一个学习的算法就是运筹学的重要分支--数学规划,而数学规划当中重要的分支就是线性规划了。在这里笔者参考了司守奎和孙玺菁老师的《数学建模算法与应用》(第三版)这本书,以此来讲讲关…

【HTML5】html5开篇基础(3)

1.❤️❤️前言~🥳🎉🎉🎉 Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的…

多元函数微分学基础题

这是基础题!!原则上必须要在第一轮初学并做完课后习题之后再做这个基础题,不能有错误(马虎大意除外)或无法解答。如有错误,该单元需要重学!! 多元函数微分学填空题 一、填空题 如…

在Pytorch中为不同层设置不同学习率来提升性能,优化深度学习模型

在深度学习模型的训练过程中,学习率作为一个关键的超参数,对模型的收敛速度和最终性能有着重大影响。传统方法通常采用统一的学习率,但随着研究的深入,我们发现为网络的不同层设置不同的学习率可能会带来显著的性能提升。本文将详…

基于Java的停车场管理微信小程序 停车场预约系统【源码+文档+讲解】

精彩专栏推荐订阅:在下方主页👇🏻👇🏻👇🏻👇🏻 💖🔥作者主页:计算机毕设木哥🔥 💖 文章目录 一、停车场管理微…

巴鲁夫rfid读头国产平替版——高频RFID读写器

随着RFID技术的不断发展,国内RFID企业的数量也在不断地变多,国产RFID读写器的质量也越来越高。具有着价格实惠、质量可靠等特点,成为了可平替国外RFID产品的首要选择。健永科技的高频RFID读写器JY-H830,是一款可平替巴鲁夫rfid读头…

基于SSM的“实习支教中小学学校信息管理系统”的设计与实现(源码+数据库+文档)

基于SSM的“实习支教中小学学校信息管理系统”的设计与实现(源码数据库文档) 开发语言:Java 数据库:MySQL 技术:SSM 工具:IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能结构图 主页 注册页面 师资力量界面 个…

机器学习(5):机器学习项目步骤(二)——收集数据与预处理

1. 数据收集与预处理的任务? 为机器学习模型提供好的“燃料” 2. 数据收集与预处理的分步骤? 收集数据-->数据可视化-->数据清洗-->特征工程-->构建特征集和数据集-->拆分数据集、验证集和测试集 3. 数据可视化工作? a. 作用&…

【ArcGIS Pro实操第三期】多模式道路网构建(Multi-model road network construction)原理及实操案例

ArcGIS Pro实操第三期:多模式道路网构建原理及实操案例 1 概述1.1 原理 2 GIS实操2.1 新建文件并导入数据2.2 创建网络数据集2.3 设置连接策略(Setting up connectivity policies)2.4 添加成本(Adding cost attributes&#xff09…

使用dockerfile来构建一个包含Jdk17的centos7镜像(构建镜像:centos7-jdk17)

文章目录 1、dockerfile简介2、入门案例2.1、创建目录 /opt/dockerfilejdk172.2、上传 jdk-17_linux-x64_bin.tar.gz 到 /opt/dockerfilejdk172.3、在/opt/dockerfilejdk17目录下创建dockerfile文件2.4、执行命令构建镜像 centos7-jdk17 : 不要忘了后面的那个 .2.5、查看镜像是…

毕业设计——springboot+netty+websocket实现一个简单的ChatGpt机器人聊天

作品详情 WebSocket是html5开始浏览器和服务端进行全双工通信的网络技术。在该协议下,与服务端只需要一次握手,之后建立一条快速通道,开始互相传输数据,实际是基于TCP双向全双工,比http半双工提高了很大的性能&#x…

AURIX单片机示例:开发入门与点亮LED

文章目录 目的模板工程Blinky_LED示例链接总结 目的 这个例程比较简单,主要通过这个例程来介绍 AURIX™ Development Studio(ADS) 和 iLLD 库来开发 AURIX 系列单片机一些入门的内容。一些更为基础的资料等内容可以参考下面文章: 《英飞凌 AURIX TriCo…

翻译:Recent Event Camera Innovations: A Survey

摘要 基于事件的视觉受到人类视觉系统的启发,提供了变革性的功能,例如低延迟、高动态范围和降低功耗。本文对事件相机进行了全面的调查,并追溯了事件相机的发展历程。它介绍了事件相机的基本原理,将其与传统的帧相机进行了比较&am…

完全二叉树的节点个数 C++ 简单问题

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。 示例 1&#xff…

基于微信小程序的智慧社区的设计与实现

博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…

使用 PowerShell 命令更改 RDP 远程桌面端口(无需修改防火墙设置)

节选自原文:Windows远程桌面一站式指南 | BOBO Blog 原文目录 什么是RDP开启远程桌面 检查系统版本启用远程桌面连接Windows 在Windows电脑上在MAC电脑上在Android或iOS移动设备上主机名连接 自定义电脑名通过主机名远程桌面使用Hosts文件自定义远程主…

LeetCode 427. 建立四叉树

LeetCode 427. 建立四叉树 (题干略) """ # Definition for a QuadTree node. class Node:def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight):self.val valself.isLeaf isLeafself.topLeft topLeftself.t…