Redis数据结构学习

news2024/11/27 21:43:22

Redis

关于redis相关的技术文章我一直没什么思路
直到最近的端午节,我偶然和一个程序员朋友聊到了关于redis数据结构相关的知识点,
所以我决定写一篇文章记录一下

首先我们需要知道redis支持哪些数据类型
  • Strings (字符串)
  • Lists(列表)
  • Hashes(哈希)
  • Sets(集合)
  • Sorted Sets(有序集合)

关于这几种数据类型的操作不是我们的重点

底层数据结构:SDS

SDS(Simple Dynamic String)是 Redis 中用来表示字符串对象的一种动态字符串结构;SDS 为 Redis 中的字符串表示提供了一种高效,灵活的实现方式.

struct sdshdr {
    // 记录 buf 数组中已使用字节的数量
    int len;
    // 记录 buf 数组中未使用字节的数量
    int free;
    // 字节数组,用于保存字符串内容
    char buf[];
};

具有以下特点:

1.动态增长的字符串长度 :

  • SDS可以根据需要动态地调整其内存大小并且在扩展时会预留一定的额外空间,通过这种方式来减少频繁的内存分配和复制操作

2.二进制安全 :

  • 简单点说就是通过len字段就可以知道实际的字符串,而不是通过C语言的\0空字符串

3.惰性空间释放 :

  • 和第一点类似,当我们需要减少字符串所占用的空间时,SDS不会立即释放掉这部分空间,减少频繁的内存分配和复制操作
底层数据结构:双向链表

列表中包含的元素都是比较长的字符串的时,Redis 就会使用链表作为 list 的底层实现

在 Redis 中,双向链表由 listNode 和 list 两个结构体组成:

  1. listNode 结构体: 表示双向链表的节点,包含了指向前驱节点和后继节点的指针,以及存储实际数据的指针。
  2. list 结构体: 表示整个双向链表,包含了指向链表表头和表尾的指针,以及链表的长度、复制函数和释放函数等信息。
typedef struct list {
  listNode *head; // 链表 头结点 指针
  listNode *tail; // 链表 尾结点 指针
  unsigned long len; // 链表长度计数器 即 节点的个数

  // 三个函数指针
  void *(*dup)(void *ptr); // 复制函数 复制链表节点保存的值
  void (*free)(void *ptr); // 释放函数 释放链表节点保存的值
  int (*match)(void *ptr, void *key); // 匹配函数 查找节点时使用 比较链表节点所保存的节点值和另一个输入的值是否相等
} list;
typedef struct listNode {
    //前置节点
    struct listNode
    //后置节点
    * prev;
    struct listNode
    //节点的值
    void *value;
    * next;
} listNode;

在这里插入图片描述

具有以下特点:

1.灵活的插入和删除操作 :

  • 双向链表支持在任意位置高效地进行插入和删除操作,由于每个节点都保存了指向前一个节点和后一个节点的指针,可以在 O(1) 时间内完成插入或删除操作。

2.反向遍历 :

  • 从前往后或者从后往前查询效率都是很客观的

3.封装了复制和释放函数 :

  • 三个函数指针,链表可以轻松地处理包含动态内存分配的数据类型
底层数据结构:字典

1.哈希表 (Hash Table)
Redis 的字典主要是通过哈希表实现的。每个字典包含两个哈希表(ht[0] 和 ht[1]),其中一个用于存储当前的数据,另一个在 rehashing 过程中使用。

2.哈希表节点 (Hash Table Node)
每个哈希表节点包含一个键值对。节点结构如下:

typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;
} dictEntry;

这个结构体定义了一个哈希表节点,包括键、值和指向下一个节点的指针(实现链地址法解决哈希冲突)。

3.哈希表结构 (Hash Table Structure)
哈希表结构包含指向哈希表数组的指针、大小、已使用节点数以及 rehash 索引:

typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} dictht;

table 是一个指向哈希表数组的指针。
size 是哈希表的大小。
sizemask 是用来计算索引的掩码。
used 是哈希表中已使用节点的数量。

4.字典结构 (Dictionary Structure)
字典结构包含两个哈希表和一些控制字段:

typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */
    unsigned long iterators; /* number of iterators currently running */
} dict;

type 是一个指向函数指针的结构体,用于实现特定类型的键和值操作。
privdata 是私有数据指针,一般用作回调函数的参数。
ht 是包含两个哈希表的数组。
rehashidx 是 rehashing 的索引,如果没有进行 rehashing,则为 -1。
iterators 表示当前运行的迭代器数量。

5.渐进式 Rehashing
Redis 使用渐进式 rehashing 来避免在哈希表扩展或缩减时阻塞服务器。rehashing 过程会逐步将 ht[0] 中的条目移动到 ht[1] 中,而不是一次性完成。这样做的好处是可以将 rehashing 的时间均摊到多个操作中,减少单次 rehashing 对性能的影响。

rehashing 过程的步骤一般如下:

  1. 初始化:创建一个新的哈希表(ht[1]),并设置 rehashidx 为 0。
  2. 渐进式迁移:在每次字典操作时(插入、删除、查找等),都会顺便将 ht[0] 中的一些条目迁移到 ht[1]。
  3. 完成:当 ht[0] 中的所有条目都迁移到 ht[1] 后,释放 ht[0],将 ht[1] 赋值给 ht[0],然后重置 ht[1] 和 rehashidx。
    通过这种方式,Redis 可以保持较高的性能,即使在哈希表需要扩展或缩减的情况下。

总结而言,Redis 的字典数据结构依赖于高效的哈希表实现,并通过渐进式 rehashing 机制来确保在动态调整哈希表大小时的性能稳定。这种设计使得 Redis 能够在处理大量数据时仍然保持快速响应。

底层数据结构:跳表

上图:

在这里插入图片描述

跳表是在双向链表之上加多层索引构成的,相对于双向链表,支持快速查找,更新,删除,所以适用于需求灵活的场景。

具有以下特点:

  • 跳表结合了链表和类似二分查找的思想;

  • 有很多层结构,由原始链表和一些通过“跳跃”生成的链表组成;

  • 每一层都是一个有序的链表;

  • 最底层(Level 1)的链表包含所有元素,越上层“跳跃”的越高,元素(索引)越少;

  • 查找时从顶层向下,不断缩小搜索范围;

  • 上层链表是下层链表的子序列;

  • 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。

跳表最经典的应用就是在Redis中实现有序集合(ZSet)。

跳表在Redis中的唯一作用也就是对该数据类型的实现,但是除了使用跳表作为有序集类型的底层数据结构外,还使用了字典来构成有序集。

底层数据结构:整数集合

我好像没有遇到过需要设置集合的应用场景,我需要在学习一下

整数集合是一种紧凑的、有序的数据结构,用于存储整数值。它可以在节省内存的同时提供快速的插入、删除和查找操作。整数集合主要用于优化存储少量整数值的情况。

typedef struct intset {
    //编码方式
    uint32
    _t encoding;
    //集合包含的元素数量
    uint32_t length;
    //保存元素的数组
    int8 t contents[];
} intset;

整数集合主要用于优化存储在 Redis 中的数据,特别适用于存储一些特定场景下的计数器、唯一标识等整数类型的数据。由于整数集合具有紧凑、高效和支持快速查找的特性,能够节省内存并提供良好的性能。

具有以下特点:

1.节约内存空间:

整数集合根据存储的整数值决定了内部的编码方式,尽可能地节省内存空间。例如,如果所有的整数值都可以用 int16 来表示,那么整数集合将采用 int16 的编码方式存储。

2.支持快速查找:

整数集合内部的整数值是有序的,采用升序排列。这使得在整数集合中进行查找操作时,可以使用二分查找算法,从而快速定位目标元素。

3.支持动态扩容:

当插入新的整数值时,如果整数集合已满,它会自动进行扩容操作。扩容过程中,整数集合会根据当前存储的最大整数类型进行扩容,并将原有的整数值转移到新的存储空间中。

4.不支持重复元素:

整数集合中的元素是唯一的,不允许重复的整数值出现。

底层数据结构:压缩列表

当列表类型的数据量较小时,Redis 会使用压缩列表来存储 LIST 数据

压缩列表由几个部分组成:
zlbytes:记录整个压缩列表占用的字节数,用于重新分配和释放内存时使用。
zltail:到达压缩列表尾节点的偏移量,方便从尾部快速获取数据。
zllen:记录压缩列表包含的节点数量。当节点数量超过 2^16-1 时,该字段值为 0xffff,此时需要遍历整个列表来获取节点数量。
entryX:压缩列表中的各个节点,每个节点包含实际的数据。
zlend:特殊值 0xff,表示压缩列表的结束。

节点由几个部分组成:
previous_entry_length:记录前一个节点的长度,以便实现反向遍历。
encoding:记录当前节点的数据类型和长度。它可以存储不同类型的数据(如字符串或整数),并以不同的编码方式表示数据长度。
content:存储实际的数据内容,具体内容取决于 encoding 字段。

1.内存效率高:由于采用紧凑的内存布局,压缩列表非常节省内存

2.适合小数据量:当数据量增加到一定程度后,压缩列表的性能会急剧下降,不适合存储大量数据

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

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

相关文章

【机器学习】集成学习方法:Bagging与Boosting的应用与优势

🔥 个人主页:空白诗 文章目录 引言一、集成学习的定义二、Bagging方法1. 随机森林(Random Forest)2. 其他Bagging方法 二、Boosting方法1. 梯度提升树(Gradient Boosting Machine, GBM)解释GBM的基本原理和…

Flutter 自定义日志模块设计

前言 村里的老人常说:“工程未动,日志先行。” 有效的利用日志,能够显著提高开发/debug效率,否则程序运行出现问题时可能需要花费大量的时间去定位错误位置和出错原因。 然而一个复杂的项目往往需要打印日志的地方比较多&#…

[Python学习篇] Python循环语句

while 循环 语法&#xff1a; while 条件: 条件成立后会重复执行的代码 ...... 示例1&#xff1a;死循环 # 这是一个死循环示例 while True:print("我正在重复执行")示例2&#xff1a;循环指定次数 i 1 while i < 5:print(f"执行次数 {i}")…

Denoising Prior Driven Deep Neural Network for Image Restoration

之所以能够检索到这篇论文是想看看该论文是如何利用多尺度相似性解决图像去噪问题&#xff0c;除了摘要和结论&#xff0c;论文中两次提到这个术语。next section是指section 4。然后整个section 4&#xff0c;根本没有提多尺度的事儿&#xff0c;更别说解决了。又看了一下The …

智慧班牌系统源码,智慧校园云平台系统,基于小程序原生开发的智慧校园小程序源码

智慧班牌系统&#xff0c;也被称为电子班牌系统&#xff0c;是一款专为学校打造的信息化产品&#xff0c;用于加强学校班级文化建设和班级风采展示。该系统通过整合学校对外宣传、日常互动交流、教师教学办公、课外学习延伸、智能硬件接入等各种服务&#xff0c;为老师、家长、…

聊天页面样式

聊天页面样式 代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><link rel"styleshee…

深度解析RocketMq源码-持久化组件(一) MappedFile

1. 绪论 rocketmq之所以能够有如此大的吞吐量&#xff0c;离不开两个组件&#xff0c;一个是利用netty实现的高性能网络通信组件&#xff1b;另一个就是利用mmap技术实现的存储组件。而在rocketmq的存储组件中主要有三个组件&#xff0c;分别是持久化文件commitLog&#xff0c…

UDP 协议详解与实战

目录 简介什么是 UDP&#xff1f;UDP 与 TCP 的区别 UDP 数据传输方式单播 - Unicast&#xff08;1:1&#xff09;广播 - Broadcast&#xff08;1:n&#xff09;有限广播 - Limited Broadcast直接广播 - Directed Broadcast 组/多播 - Multicast&#xff08;n:m&#xff09;任播…

Golang——gRPC认证和拦截器

一. OpenSSL 1.1 介绍 OpenSSL是一个开放源代码的软件库包&#xff0c;用于支持网络通讯过程中的加密。这个库提供的功能包含了SSL和TLS协议的实现&#xff0c;并可用于生成密钥、证书、进行密码运算等。 其组成主要包括一下三个组件&#xff1a; openssl&#xff1a;多用途的命…

智能化状态管理:自动状态流转处理模块

目录 基本背景介绍 具体实现 基本数据准备 基本数据表 状态转换常量 状态转换注解 任务处理模版 各任务实现逻辑 开启比对任务进行处理 降噪字段处理任务处理 开启业务数据比对处理 业务数据比对处理 开始核对数据生成最终报告处理 核对数据生成最终报告处理 状…

[渗透测试学习] SolarLab-HackTheBox

SolarLab-HackTheBox 信息搜集 nmap扫描端口 nmap -sV -v 10.10.11.16扫描结果如下 PORT STATE SERVICE VERSION 80/tcp open http nginx 1.24.0 135/tcp open msrpc Microsoft Windows RPC 139/tcp open netbios-ssn Microsoft Windows n…

观光车司机N2精选考试题库(附答案)

一、判断题 1、在使用手电钻、电砂轮等手持电动工具时,为保证安全,应该装设漏电保护器。(√) 2、碳弧气刨的方法设备工具简单.操作使用安全。(√) 3、事故调查组有权向有关单位和个人了解与事故有关的情况。()(√) 4、发射药(动力药)是能产生发射和推进效应的烟火药,有粒状、粉…

SAP BOM项目类别N非库存项目简介

在BOM的项目类别中用的最多的就是L类型的库存管理,还有T类型的文本类型,但是在实际业务中也会存在物料不做库存管理,但是物料需要进行成本的管控,进入对应的工单成本中,比如在电子行业中需要烧录的正版软件,或者是电脑制造行业中需要预装的正版的Windows系统,购买的软件…

【SpringBoot】SpringBoot:简化数据库操作与API开发

文章目录 引言SpringBoot概述数据库操作简化传统数据库操作的挑战使用Spring Data JPA示例&#xff1a;定义Repository接口实现服务层 使用MyBatis示例&#xff1a;配置MyBatis定义Mapper接口 API开发简化RESTful API概述创建RESTful API示例&#xff1a;定义控制器 高级特性与…

【二】【动态规划NEW】91. 解码方法,62. 不同路径,63. 不同路径 II

91. 解码方法 一条包含字母 A-Z 的消息通过以下映射进行了 编码 &#xff1a; ‘A’ -> “1” ‘B’ -> “2” … ‘Z’ -> “26” 要 解码 已编码的消息&#xff0c;所有数字必须基于上述映射的方法&#xff0c;反向映射回字母&#xff08;可能有多种方法&#xff…

小知识点快速总结:Batch Normalization Layer(BN层)的作用

本系列文章只做简要总结&#xff0c;不详细说明原理和公式。 目录 1. 参考文章2. 主要作用3. 具体分析3.1 正则化&#xff0c;降低过拟合3.2 提高模型收敛速度&#xff0c;加速训练3.3 减少梯度爆炸或者梯度消失的情况 4. 补充4.1 BN层做的是标准化不是归一化4.2 BN层的公式4.…

洗地机提升渗透率,降价不是唯一解

作者 | 辰纹 来源 | 洞见新研社 添可2019年开创洗地机赛道时&#xff0c;看好的人不多&#xff0c;在扫地机器人正被风口吹在天上翻滚的那个年代&#xff0c;洗地机被扣上了“智商税”的标签。 洗地机到底有没有用&#xff0c;市场用脚投票。 奥维云网数据显示&#xff0c…

PS通过GTX实现SFP网络通信2

PS 程序设计 LWIP 库修改 修改原因 SDK 2017.4 自带的 LWIP 1.4.1 库的版本为 2.0 &#xff0c;直接使用该库将无法通过 SFP 实现网络通信。 因此需要进行修改。 修改的原因有 2 个&#xff0c;第 1 个原因是由于 2017.4 版本产生的新 bug 。在 2015.4 版本…

Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)

前言&#xff1a;ArrayList是Java中最常用的动态数组实现之一&#xff0c;它提供了便捷的操作接口和灵活的扩展能力&#xff0c;使得在处理动态数据集合时非常方便。本文将深入探讨Java中ArrayList的实现原理、常用操作以及一些使用场景。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨…

Kotlin 语言基础学习

什么是Kotlin ? Kotiln翻译为中文是:靠他灵。它是由JetBrains 这家公司开发的,JetBrains 是一家编译器软件起家的,例如常用的WebStorm、IntelliJ IDEA等软件。 Kotlin官网 JetBrains 官网 Kotlin 语言目前的现状: 目前Android 已将Kotlin 作为官方开发语言。 Spring 框…