Redis数据结构学习

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.适合小数据量:当数据量增加到一定程度后,压缩列表的性能会急剧下降,不适合存储大量数据

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

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

相关文章

Transformer模型:未来的改进方向与潜在影响

Transformer模型:未来的改进方向与潜在影响 自从2017年Google的研究者们首次提出Transformer模型以来,它已经彻底改变了自然语言处理(NLP)领域的面貌。Transformer的核心优势在于其“自注意力(Self-Attention&#xf…

【C语言习题】31.冒泡排序

文章目录 作业标题作业内容2.解题思路3.具体代码 作业标题 冒泡排序 作业内容 实现一个对整形数组的冒泡排序 2.解题思路 先了解一下冒泡排序: 两两相邻的元素进行比较,如果前面元素大于后面元素就交换两个元素的位置,最终的结果是最大的…

RERCS系统开发实战案例-Part08 FPM 应用程序的表单组件(From UIBB)与列表组件(List UIBB)组合的创建

1、新建From UIBB的FPM Application的快速启动面板 备注:该步骤可第一步操作,也可最后一步操作,本人习惯第一步操作。 1)使用事务码 LPD_CUST,选择对应的角色与实例进入快速启动板定制页面; 2&#xff09…

pg表空间和mysql表空间的区别

一、表空间的定义 1、在pg中表空间实际上是为表指定一个存储的目录。并且在创建数据库时可以为数据库指定默认的表空间。创建表和索引时可以指定表空间,这样表和索引就可以存储到表空间对应的目录下了。 在pg中一个库中可以有多个表空间,一个表空间可以…

U盘量产经历二——phisonPS2251-70(PS2270)

写在前面: 量产相关的BBS看了挺多,phison群联的芯片PS2251-70(PS2270)的量产工具比较少,而且很难下载。这里我访问了国外的网站下载来了,也贴出来给童鞋们取用。 以下是记录的量产过程: https://www.usbdev.ru 工具…

Linux操作系统学习:day04

内容来自:Linux介绍 视频推荐:[Linux基础入门教程-linux命令-vim-gcc/g -动态库/静态库 -makefile-gdb调试]( 目录 day0422、通过文字设定法修改用户对文件的操作权限23、通过数字设定法修改文件的权限24、修改文件所有者和所属组25、tree—查看目录内…

国际荐酒师香港协会受邀参加2024年美国独立日庆祝活动

国际荐酒师(香港)协会受邀参加2024年美国独立日庆祝活动促进世界酒中国菜的全球化发展 2024年6月18日,国际荐酒师(香港)协会大中华区驻广州办事处荣幸地接受了美国驻广州总领事馆 Nicholas Burns大使和Lisa Heller总领…

python修改pip install 默认安装路径

第一步:通过win菜单,找到Prompt,点击进入 第二步:在cmd里输入 python -m site获得: D:\ProgramData\Anaconda3 ----》是Anaconda安装的位置USER_BASE: C:\Users\kevin… ----》表示默认路径在C盘USER_SITE: C:\Users\kevin… ----》表示默认路径在C盘1.2 修改pip 默认安…

使用Minikube部署Kubernetes环境

使用Minikube部署Kubernetes环境 1. Minikube简介 Minikube是一个轻量级的Kubernetes实现,它在本地运行一个Kubernetes集群,可以是单节点或者集群环境,主要用于开发和测试。Minikube支持Kubernetes的所有主要功能,包括Dashboard…

C#——方法的参数列表ref、out、params、in详情

在C#中,方法参数列表是在定义方法时指定的,用于接收传递给方法的数据。参数列表包括参数类型和参数名。参数可以是必需的(必须有值),也可以是可选的(可以有默认值)。 方法的参数列表 1. 值参数…

温湿度采集与OLED显示

目录 一、什么是软件I2C 二、什么是硬件I2C 三、STM32CubeMX配置 1、RCC配置 2、SYS配置 3、I2C1配置 3、I2C2配置 4、USART1配置 5、TIM1配置 6、时钟树配置 7、工程配置 四、设备链接 1、OLED连接 2、串口连接 3、温湿度传感器连接 五、每隔2秒钟采集一次温湿…

jquey+mybatis-plus实现简单分页功能

这篇文章介绍一下怎么通过JQuery结合mybatis-plus的分页插件实现原生HTML页面的分页效果,没有使用任何前端框架,主要是对前端知识的应用。 创建Springboot项目 Intellij IDEA中创建一个Springboot项目,项目名为pager。 添加必须的依赖包 修…

Linux安装Tomcat和Nginx

目录 前言一、系统环境二、Tomcat安装步骤Step1 安装JDK环境Step2 安装Tomcat 三、Nginx安装步骤四、测试4.1 测试Tomcat4.2 测试Nginx 总结 前言 本篇文章介绍如何在Linux上安装Tomcat web服务器。 一、系统环境 虚拟机版本:VMware Workstation 15 ProLinux镜像…

Java基础 - 练习(二)打印菱形

Java基础练习 打印菱形&#xff0c;先上代码&#xff1a; // 方法一&#xff1a;基础&#xff0c;好理解 public static void diamond() {//控制行数for (int i 1; i < 4; i) {//空格的个数for (int k 1; k < 4 - i; k) {System.out.print(" ");}//控制星星…

链表OJ--超详细解析

链表OJ 文章目录 链表OJ1. 反转链表2. 返回K值3. 链表的中间节点4. 回文链表5. 相交链表6. 带环链表6.1 为什么一定会相遇&#xff0c;有没有可能会错过&#xff0c;或者出现永远追不上的情况&#xff0c;请证明6.2 slow一次走一步&#xff0c;fast如果一次走3步&#xff0c;走…

解决nvm切换node版本后,全局依赖无法使用

问题描述 使用 nvm install 10.24.1 安装node版本&#xff0c;安装成功后&#xff0c;使用 npm install -g xxx 安装全局依赖&#xff08;私有库&#xff09;&#xff0c;安装成功后&#xff0c;运行命令提示找不到命令。 已做以下尝试 npm root -g&#xff0c;返回 D:\Prog…

【Java面试】二十、JVM篇(上):JVM结构

文章目录 1、JVM2、程序计数器3、堆4、栈4.1 垃圾回收是否涉及栈内存4.2 栈内存分配越大越好吗4.3 方法内的局部变量是否线程安全吗4.4 栈内存溢出的情况4.5 堆和栈的区别是什么 5、方法区5.1 常量池5.2 运行时常量池 6、直接内存 1、JVM Java源码编译成class字节码后&#xf…

window端口占用情况及state解析

背景&#xff1a; 在电脑使用过程中&#xff0c;经常会开许多项目&#xff0c;慢慢地发现电脑越来越卡&#xff0c;都不知道到底是在跑什么项目导致&#xff0c;于是就想查看一下电脑到底在跑什么软件和项目&#xff0c;以作记录。 常用命令 netstat -tuln &#xff1a; 使用…

这些已经死去的软件,依旧无可替代

互联网这条长河里&#xff0c;软件们就像流星一样&#xff0c;一闪而过。有的软件火过一段时间&#xff0c;然后就慢慢消失了。 说不定有些软件你以前天天用&#xff0c;但不知道从什么时候开始就不再用了。时间一天天过去&#xff0c;我们的热情、记忆都在消退&#xff0c;还…

【免费API推荐】: 解锁创意无限,享受免费开发之旅

幂简网站上免费的 API 分类内汇集了各种各样的免费 API&#xff0c;涵盖了多个领域和功能。无论你是在构建网站、开发应用还是进行数据分析&#xff0c;这个项目都能为你提供丰富的选择。 幂简集成搜集了网络上免费的 API 资源&#xff0c;为广大开发者和创业者提供便捷的访问渠…