Redis设计与实现之RDB

目录

一、RDB

1、保存

2、 SAVE 、BGSAVE 、AOF 写入和 BGREWRITEAOF

SAVE

BGSAVE

3、载入

4、RDB 文件结构

REDIS

DB-DATA

SELECT-DB

KEY-VALUE-PAIRS

EOF

CHECK-SUM

二、 小结


一、RDB

在运行情况下,Redis 以数据结构的形式将数据维持在内存中,为了让这些数据在 Redis 重启 之后仍然可用,Redis 分别提供了 RDB 和 AOF 两种持久化模式。

在 Redis 运行时,RDB 程序将当前内存中的数据库快照保存到磁盘文件中,在 Redis 重启动 时,RDB 程序可以通过载入 RDB 文件来还原数据库的状态。

RDB 功能最核心的是 rdbSave 和 rdbLoad 两个函数,前者用于生成 RDB 文件到磁盘,而后

者则用于将 RDB 文件中的数据重新载入到内存中:

本章先介绍 SAVE 和 BGSAVE 命令的实现,以及 rdbSave 和 rdbLoad 两个函数的运行机制, 然后以图表的方式,分部分来介绍 RDB 文件的组织形式。

因为本章涉及 RDB 运行的相关机制,如果还没了解过 RDB 功能的话,请先阅读 Redis 官网 上的 persistence 手册 。

1、保存

rdbSave 函数负责将内存中的数据库数据以 RDB 格式保存到磁盘中,如果 RDB 文件已存在,那么新的 RDB 文件将替换已有的 RDB 文件。
在保存 RDB 文件期间,主进程会被阻塞,直到保存完成为止。
SAVE 和 BGSAVE 两个命令都会调用 rdbSave 函数,但它们调用的方式各有不同:

• SAVE 直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。在主进程阻塞期间, 服务器不能处理客户端的任何请求。

• BGSAVE 则 fork 出一个子进程,子进程负责调用 rdbSave ,并在保存完成之后向主 进程发送信号,通知保存已完成。因为 rdbSave 在子进程被调用,所以 Redis 服务器在 BGSAVE 执行期间仍然可以继续处理客户端的请求。通过伪代码来描述这两个命令,可以很容易地看出它们之间的区别:

def SAVE(): 
    rdbSave()
def BGSAVE(): 
    pid = fork()
    if pid == 0:
    # 子进程保存 RDB
        rdbSave() 
    elif pid > 0:
        # 父进程继续处理请求,并等待子进程的完成信号 
         handle_request()
    else:
        # pid == -1
        # 处理 fork 错误 
        handle_fork_error()

2、 SAVE 、BGSAVE 、AOF 写入和 BGREWRITEAOF

除了了解 RDB 文件的保存方式之外,我们可能还想知道,两个 RDB 保存命令能否同时使用?

它们和 AOF 保存工作是否冲突? 本节就来解答这些问题。

SAVE

前面提到过,当 SAVE 执行时,Redis 服务器是阻塞的,所以当 SAVE 正在执行时,新的 SAVE、BGSAVE 或 BGREWRITEAOF 调用都不会产生任何作用。

只有在上一个 SAVE 执行完毕、Redis 重新开始接受请求之后,新的 SAVE 、BGSAVE 或 BGREWRITEAOF 命令才会被处理。

另外,因为 AOF 写入由后台线程完成,而 BGREWRITEAOF 则由子进程完成,所以在 SAVE 执行的过程中,AOF 写入和 BGREWRITEAOF 可以同时进行。

BGSAVE

在执行 SAVE 命令之前,服务器会检查 BGSAVE 是否正在执行当中,如果是的话,服务器就 不调用 rdbSave ,而是向客户端返回一个出错信息,告知在 BGSAVE 执行期间,不能执行 SAVE 。

这样做可以避免 SAVE 和 BGSAVE 调用的两个 rdbSave 交叉执行,造成竞争条件。 另一方面,当 BGSAVE 正在执行时,调用新 BGSAVE 命令的客户端会收到一个出错信息,告知 BGSAVE 已经在执行当中。 BGREWRITEAOF 和 BGSAVE 不能同时执行:

• 如果 BGSAVE 正在执行,那么 BGREWRITEAOF 的重写请求会被延迟到 BGSAVE 执 行完毕之后进行,执行 BGREWRITEAOF 命令的客户端会收到请求被延迟的回复。

• 如果 BGREWRITEAOF 正在执行,那么调用 BGSAVE 的客户端将收到出错信息,表示 这两个命令不能同时执行。

BGREWRITEAOF 和 BGSAVE 两个命令在操作方面并没有什么冲突的地方,不能同时执行 它们只是一个性能方面的考虑:并发出两个子进程,并且两个子进程都同时进行大量的磁盘写 入操作,这怎么想都不会是一个好主意。

3、载入

当 Redis 服务器启动时,rdbLoad 函数就会被执行,它读取 RDB 文件,并将文件中的数据库数据载入到内存中。

在载入期间,服务器每载入 1000 个键就处理一次所有已到达的请求,不过只有 PUBLISH 、SUBSCRIBE 、PSUBSCRIBE 、UNSUBSCRIBE 、PUNSUBSCRIBE 五个命令的请求会被正确地处理, 其他命令一律返回错误。等到载入完成之后,服务器才会开始正常处理所有命令。

Note: 发布与订阅功能和其他数据库功能是完全隔离的,前者不写入也不读取数据库,所以 在服务器载入期间,订阅与发布功能仍然可以正常使用,而不必担心对载入数据的完整性产生 影响。

另外,因为 AOF 文件的保存频率通常要高于 RDB 文件保存的频率,所以一般来说,AOF 文 件中的数据会比 RDB 文件中的数据要新。

因此,如果服务器在启动时,打开了 AOF功能,那么程序优先使用 AOF 文件来还原数据。只有在 AOF 功能未打开的情况下,Redis 才会使用 RDB 文件来还原数据。

4、RDB 文件结构

前面介绍了保存和读取 RDB 文件的两个函数,现在,是时候介绍 RDB 文件本身了。一个 RDB 文件可以分为以下几个部分:

 以下的几个小节将分别对这几个部分的保存和读入规则进行介绍。

REDIS

文件的最开头保存着 REDIS 五个字符,标识着一个 RDB 文件的开始。 在读入文件的时候,程序可以通过检查一个文件的前五个字节,来快速地判断该文件是否有可

能是 RDB 文件。 RDB-VERSION

一个四字节长的以字符表示的整数,记录了该文件所使用的 RDB 版本号。 目前的 RDB 文件版本为 0006 。

因为不同版本的 RDB 文件互不兼容,所以在读入程序时,需要根据版本来选择不同的读入方 式。

DB-DATA

这个部分在一个 RDB 文件中会出现任意多次,每个 DB-DATA 部分保存着服务器上一个非空数 据库的所有数据。

SELECT-DB

这域保存着跟在后面的键值对所属的数据库号码。

在读入 RDB 文件时,程序会根据这个域的值来切换数据库,确保数据被还原到正确的数据库 上。

KEY-VALUE-PAIRS

因为空的数据库不会被保存到 RDB 文件,所以这个部分至少会包含一个键值对的数据。 每个键值对的数据使用以下结构来保存:

+----------------------+---------------+-----+-------+
| OPTIONAL-EXPIRE-TIME | TYPE-OF-VALUE | KEY | VALUE |
+----------------------+---------------+-----+-------+

OPTIONAL-EXPIRE-TIME 域是可选的,如果键没有设置过期时间,那么这个域就不会出现;反 之,如果这个域出现的话,那么它记录着键的过期时间,在当前版本的 RDB 中,过期时间是 一个以毫秒为单位的 UNIX 时间戳。

KEY 域保存着键,格式和 REDIS_ENCODING_RAW 编码的字符串对象一样(见下文)。 TYPE-OF-VALUE 域记录着 VALUE 域的值所使用的编码,根据这个域的指示,程序会使用不同的方式来保存和读取 VALUE 的值。

Note: 下文提到的编码在《对象处理机制》章节介绍过,如果忘记了可以回去重温下。

保存 VALUE 的详细格式如下:
• REDIS_ENCODING_INT 编码的 REDIS_STRING 类型对象:

如果值可以表示为 8 位、16 位或 32 位有符号整数,那么直接以整数类型的形式来保存 它们:

+---------+
| integer |
+---------+

比如说,整数 8 可以用 8 位序列 00001000 保存。 当读入这类值时,程序按指定的长度读入字节数据,然后将数据转换回整数类型。

另一方面,如果值不能被表示为最高 32 位的有符号整数,那么说明这是一个 long long 类型的值,在 RDB 文件中,这种类型的值以字符序列的形式保存。一个字符序列由两部分组成:

+-----+---------+
| LEN | CONTENT |
+-----+---------+

其中,CONTENT 域保存了字符内容,而 LEN 则保存了以字节为单位的字符长度。 当进行载入时,读入器先读入 LEN ,创建一个长度等于 LEN 的字符串对象,然后再从文

件中读取 LEN 字节数据,并将这些数据设置为字符串对象的值。
• REDIS_ENCODING_RAW 编码的 REDIS_STRING 类型值有三种保存方式:

1.如果值可以表示为8位、16位或32位长的有符号整数,那么用整数类型的形式来保存它们。

2.如果字符串长度大于 20 ,并且服务器开启了 LZF 压缩功能 ,那么对字符串进行压 缩,并保存压缩之后的数据。

经过 LZF 压缩的字符串会被保存为以下结构:

+----------+----------------+--------------------+
| LZF-FLAG | COMPRESSED-LEN | COMPRESSED-CONTENT |
+----------+----------------+--------------------+

LZF-FLAG 告知读入器,后面跟着的是被 LZF 算法压缩过的数据。

COMPRESSED-CONTENT 是被压缩后的数据,COMPRESSED-LEN 则是该数据的字节长度。

3.在其他情况下,程序直接以普通字节序列的方式来保存字符串。比如说,对于一个长度为 20 字节的字符串,需要使用 20 字节的空间来保存它。 这种字符串被保存为以下结构:

+-----+---------+
| LEN | CONTENT |
+-----+---------+

 LEN 为字符串的字节长度,CONTENT 为字符串。

当进行载入时,读入器先检测字符串保存的方式,再根据不同的保存方式,用不同的方法

取出内容,并将内容保存到新建的字符串对象当中。
• REDIS_ENCODING_LINKEDLIST 编码的 REDIS_LIST 类型值保存为以下结构:

+-----------+--------------+--------------+-----+--------------+
| NODE-SIZE | NODE-VALUE-1 | NODE-VALUE-2 | ... | NODE-VALUE-N |
+-----------+--------------+--------------+-----+--------------+

其中 NODE-SIZE 保存链表节点数量,后面跟着任意多个节点值。节点值的保存方式和字 符串的保存方式一样。

当进行载入时,读入器读取节点的数量,创建一个新的链表,然后一直执行以下步骤,直 到指定节点数量满足为止:

1. 读取字符串表示的节点值

2. 将包含节点值的新节点添加到链表中
• REDIS_ENCODING_HT 编码的 REDIS_SET 类型值保存为以下结构:

+----------+-----------+-----------+-----+-----------+
| SET-SIZE | ELEMENT-1 | ELEMENT-2 | ... | ELEMENT-N |
+----------+-----------+-----------+-----+-----------+

SET-SIZE 记录了集合元素的数量,后面跟着多个元素值。元素值的保存方式和字符串的 保存方式一样。

载入时,读入器先读入集合元素的数量 SET-SIZE ,再连续读入 SET-SIZE 个字符串,并 将这些字符串作为新元素添加至新创建的集合。

• REDIS_ENCODING_SKIPLIST 编码的 REDIS_ZSET 类型值保存为以下结构:

+--------------+-------+---------+-------+---------+-----+-------+---------+
| ELEMENT-SIZE | MEB-1 | SCORE-1 | MEB-2 | SCORE-2 | ... | MEB-N | SCORE-N |
+--------------+-------+---------+-------+---------+-----+-------+---------+

其中 ELEMENT-SIZE 为有序集元素的数量,MEMBER-i 为第 i 个有序集元素的成员,

SCORE-i 为第 i 个有序集元素的分值。 当进行载入时,读入器读取有序集元素数量,创建一个新的有序集,然后一直执行以下步骤,直到指定元素数量满足为止:
1. 读入字符串形式保存的member
2. 读入字符串形式保存的score,并将它转换为浮点数

3. 添加member为成员、score为分值的新元素到有序集

  • REDIS_ENCODING_HT 编码的 REDIS_HASH 类型值保存为以下结构:

    +-----------+-------+---------+-------+---------+-----+-------+---------+
    | HASH-SIZE | KEY-1 | VALUE-1 | KEY-2 | VALUE-2 | ... | KEY-N | VALUE-N |
    +-----------+-------+---------+-------+---------+-----+-------+---------+

  • HASH-SIZE 是哈希表包含的键值对的数量,KEY-i 和 VALUE-i 分别是哈希表的键和值。 载入时,程序先创建一个新的哈希表,然后读入 HASH-SIZE ,再执行以下步骤 HASH-SIZE次:

    1. 读入一个字符串

    2. 再读入另一个字符串

    3. 将第一个读入的字符串作为键,第二个读入的字符串作为值,插入到新建立的哈希 中。

  • REDIS_LIST 类 型、 REDIS_HASH 类 型 和 REDIS_ZSET 类 型 都 使 用 了 REDIS_ENCODING_ZIPLIST 编码,ziplist 在 RDB 中的保存方式如下:

    +-----+---------+
    | LEN | ZIPLIST |
    +-----+---------+

    载入时,读入器先读入 ziplist 的字节长,再根据该字节长读入数据,最后将数据还原 成一个 ziplist 。

  • • REDIS_ENCODING_INTSET 编码的 REDIS_SET 类型值保存为以下结构:

    +-----+--------+
    | LEN | INTSET |
    +-----+--------+

    载入时,读入器先读入 intset 的字节长度,再根据长度读入数据,最后将数据还原成 intset 。

EOF

标志着数据库内容的结尾(不是文件的结尾),值为 rdb.h/EDIS_RDB_OPCODE_EOF (255)。

CHECK-SUM

RDB 文件所有内容的校验和,一个 uint_64t 类型值。
REDIS 在写入 RDB 文件时将校验和保存在 RDB 文件的末尾,当读取时,根据它的值对内容进行校验。

如果这个域的值为 0 ,那么表示 Redis 关闭了校验和功能。

二、 小结

  • rdbSave 会将数据库数据保存到 RDB 文件,并在保存完成之前阻塞调用者。

  • SAVE 命令直接调用 rdbSave ,阻塞 Redis 主进程;BGSAVE 用子进程调用 rdbSave , 主进程仍可继续处理命令请求。

  • SAVE 执行期间,AOF 写入可以在后台线程进行,BGREWRITEAOF 可以在子进程进 行,所以这三种操作可以同时进行。

  • 为了避免产生竞争条件,BGSAVE 执行时,SAVE 命令不能执行。

  • 为了避免性能问题,BGSAVE 和 BGREWRITEAOF 不能同时执行。

  • 调用 rdbLoad 函数载入 RDB 文件时,不能进行任何和数据库相关的操作,不过订阅与 发布方面的命令可以正常执行,因为它们和数据库不相关联。

  • RDB 文件的组织方式如下:

    +-------+-------------+-----------+-----------------+-----+-----------+
    | REDIS | RDB-VERSION | SELECT-DB | KEY-VALUE-PAIRS | EOF | CHECK-SUM |
    +-------+-------------+-----------+-----------------+-----+-----------+
                          |<-------- DB-DATA ---------->|
  • 键值对在 RDB 文件中的组织方式如下:

    +----------------------+---------------+-----+-------+
    | OPTIONAL-EXPIRE-TIME | TYPE-OF-VALUE | KEY | VALUE |
    +----------------------+---------------+-----+-------+

    RDB 文件使用不同的格式来保存不同类型的值。

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

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

相关文章

头歌—衍生密码体制

# 第1关&#xff1a;Rabin密码体制 题目描述 任务描述 Rabin密码体制是RSA密码体制的一种。 本关任务&#xff1a;使用Rabin密码体制对给定的明文进行加密。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;Rabin密码体制。 Rabin密码体制 在本关中&#x…

幻彩LED灯带芯片:SM16703SP单点单控 断点续传

幻彩LED灯带芯片SM16703SP3是一款单点单控断点续传的芯片&#xff0c;它采用了先进的技术&#xff0c;可以实现灯光的变化和控制。这款芯片不仅仅可以提供各种丰富多彩的灯光效果&#xff0c;还有断点续传功能&#xff0c; LED断点续传灯条采用了双信号线交叉传输的方案&#x…

【Spring Boot】面试题汇总,带答案的那种

继上次的文章【MySQL连环炮&#xff0c;你抗的住嘛&#xff1f;】爆火之后&#xff0c;越来越多的小伙伴后台留言&#xff0c;要求阿Q总结下其他的“连环炮”知识点&#xff0c;想在金九银十的面试黄金期轻松对线面试官。 同样为了节省大家的时间&#xff0c;阿Q最近对【Sprin…

链接未来:深入理解链表数据结构(二.c语言实现带头双向循环链表)

上篇文章简述讲解了链表的基本概念并且实现了无头单向不循环链表&#xff1a;链接未来&#xff1a;深入理解链表数据结构&#xff08;一.c语言实现无头单向非循环链表&#xff09;-CSDN博客 那今天接着给大家带来带头双向循环链表的实现&#xff1a; 文章目录 一.项目文件规划…

在线商城系统软件源码与报价_OctShop

随着互联网、5G、人工智能的快速发展&#xff0c;人们在家购物已经是生活的重要方式。各种在线商城系统的不断涌现&#xff0c;同时&#xff0c;也给传统的企业商家销售带来了不小的压力&#xff0c;那么&#xff0c;如何调整&#xff0c;以适应时代的发展呢&#xff1f;经过不…

【数据结构和算法】最大连续1的个数 III

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 2.1 方法一&#xff1a;滑动窗口 2.2 滑动窗口解题模板 三、代码 3.1 方法一&#xff1a;滑动窗口 四、…

世界第一!移动云刷新虚拟化性能测试世界纪录

近日&#xff0c;国际权威性能测评机构SPEC公布了最新一期虚拟化性能基准测试结果&#xff0c;移动云大云天元操作系统&#xff08;BC-Linux&#xff09;&#xff0c;凭借其出色的虚拟化性能&#xff0c;一举将世界纪录提升了10%&#xff0c;总分达到了8336分。 移动云SPEC vir…

Mybatis-plus动态条件查询QueryWrapper的函数用法

目录 前言1. QueryWrapper2. 函数3. Demo 前言 原本都是在Mapper文件中修改&#xff0c;直到看到项目中使用了QueryWrapper这个函数&#xff0c;大致了解了用法以及功能&#xff0c;发现还可以&#xff01; 对此此贴为科普帖以及笔记帖 1. QueryWrapper MyBatis-Plus 是 My…

Angular 进阶之五: Signals到底用不用?

Angular 在V16的时候推出了Signals&#xff0c;在17正式作为主打功能之一强烈推荐&#xff0c;看过了各种博主的各种科普文章也没说明白&#xff0c;到底这东西值不值得用&#xff1f;毕竟项目大了&#xff0c;重构代码也不是闹着玩儿的。各种科普文章主要在说两点&#xff1a;…

pake协议传输文件magic-wormhole

pake协议传输文件magic-wormhole 1 magic-wormhole简介其他介绍 2 安装magic-wormhole3 使用示范发送文件指定虫洞码长度 接收文件 1 magic-wormhole简介 16.7k star 强推&#xff0c;丝滑、简洁、安全的开源工具——magic-wormhole 项目地址&#xff1a;https://github.com/…

Android应用-flutter使用Positioned将控件定位到底部中间

文章目录 场景描述示例解释 场景描述 要将Positioned定位到屏幕底部中间的位置&#xff0c;你可以使用MediaQuery来获取屏幕的高度&#xff0c;然后设置Positioned的bottom属性和left或right属性&#xff0c;一般我们left和right都会设置一个值让控制置于合适的位置&#xff0…

Bert-vits2-2.3-Final,Bert-vits2最终版一键整合包(复刻生化危机艾达王)

近日&#xff0c;Bert-vits2发布了最新的版本2.3-final&#xff0c;意为最终版&#xff0c;修复了一些已知的bug&#xff0c;添加基于 WavLM 的 Discriminator&#xff08;来源于 StyleTTS2&#xff09;&#xff0c;令人意外的是&#xff0c;因情感控制效果不佳&#xff0c;去除…

【大模型】快速体验百度智能云千帆AppBuilder搭建知识库与小助手

文章目录 前言千帆AppBuilder什么是千帆AppBuilderAppBuilder能做什么 体验千帆AppBuilderJava知识库高考作文小助手 总结 前言 前天&#xff0c;在【百度智能云智算大会】上&#xff0c;百度智能云千帆AppBuilder正式开放服务。这是一个AI原生应用开发工作台&#xff0c;可以…

计算机网络:应用层

0 本节主要内容 问题描述 解决思路 1 问题描述 不同的网络服务&#xff1a; DNS&#xff1a;用来把人们使用的机器名字&#xff08;域名&#xff09;转换为 IP 地址&#xff1b;DHCP&#xff1a;允许一台计算机加入网络和获取 IP 地址&#xff0c;而不用手工配置&#xff1…

【DWJ_1703225514】基于Sklearn航空公司服务质量分析

【Talk is cheap】 # 导入库 import warnings warnings.filterwarnings(ignore)import pandas as pd import seaborn as sns import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False %matplotlib inlinefrom skl…

华为科技:辉煌发展、问题应对与未来战略

导言 作为全球领先的科技公司之一&#xff0c;华为经历了辉煌的发展历程。本文将深入探讨华为科技的发展过程、遇到的问题及解决过程、未来的可用范围&#xff0c;以及在各国的应用和未来的研究趋势。同时&#xff0c;分析在哪些领域华为能够取胜&#xff0c;以及在哪些方面发力…

文献管理软件EndNote X9 mac功能介绍

EndNote X9 for Mac是一款文献管理软件&#xff0c;不仅可以让您免于手动收集和整理您的研究资料和格式化书目的繁琐工作&#xff0c;还可以让您在与同事协调时更加轻松自如。让你的团队专注科研&#xff0c;更高效的共享文献开展协作。 EndNote X9 for Mac功能介绍 引文报告 …

数据结构和算法-红黑树(定义 性质 查找 插入 删除)

文章目录 红黑树的定义和性质为什么要发明红黑树&#xff1f;红黑树怎么考总览红黑树的定义实例&#xff1a;一颗红黑树练习&#xff1a;是否符合红黑树的要求一种可能的出题思路补充概念&#xff1a;节点黑高 红黑树的性质 红黑树的查找红黑树的插入实例小结与黑高相关的理论 …

深入浅出:Swagger annotations (注解)在API文档中的应用

Swagger 提供的注解集是其框架中定义 API 规范和文档的重要工具。这些注解在代码里标注重要部分&#xff0c;为 Swagger 的解析工作铺路&#xff0c;进而生成详尽的 API 文档。开发者编写的注释能够被转换成直观的文档&#xff0c;并展现API端点、参数和响应等信息。这不仅提升…

创新固定资产管理方式:易点易动集成企业微信的全新解决方案

在当今竞争激烈的商业环境中&#xff0c;高效的固定资产管理对于企业的成功至关重要。然而&#xff0c;传统的资产管理方式往往繁琐、容易出错&#xff0c;并且缺乏实时性和准确性。为了解决这些挑战&#xff0c;易点易动与企业微信进行了集成合作&#xff0c;推出了一种全新的…