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

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

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

相关文章

通过语言大模型来学习LLM和LMM(四)

一、大模型学习 新的东西,学习的东西就是多,而且最简单最基础的都需要学习,仿佛一点基础知识都要细嚼慢咽,刨根问底,再加上一顿云里雾里的吹嘘,迷迷糊糊的感觉高大上。其实就是那么一回事。再过一段时日&a…

Java课程设计:基于swing的学生信息管理系统

文章目录 一、项目介绍二、项目展示三、源码展示四、源码获取 一、项目介绍 这款Java swing实现的学生信息管理系统和jsp版本的功能很相似,简单的实现了班级信息的增删改查,学生信息的增删改查,数据库采用的是mysql,jdk版本不限&…

Django+Vue.js怎么实现搜索功能

一.前言 类似这样的搜索功能 二.前端代码 <div class"form-container"><div class"form-group"><label for"departure-city">出发城市</label><select v-model"departureCity" id"departure-city&q…

C# Winform 用户控件,扩展控件,自定义控件综合实例

Control类是Windows窗体控件的基类&#xff0c;它提供了在 Windows 窗体应用程序中进行可视显示所需的基础结构&#xff0c;可以通过继承来扩展熟悉的用户控件和现有控件的功能。本列介绍三种不同自定义控件以及怎么创建他们。 自定义控件分类 用户控件&#xff1a;基本控件的…

Python 中国象棋游戏【含Python源码 MX_011期】

简介&#xff1a; 中国象棋是一种古老而深受喜爱的策略棋类游戏&#xff0c;也被称为中国的国粹之一。它在中国有着悠久的历史&#xff0c;起源可以追溯到几个世纪以前。Python 中国象棋游戏是一个用Python编程语言编写的软件程序&#xff0c;旨在模拟和提供中国象棋的游戏体验…

性能测试包括哪些方面?

性能测试、通过自动化测试工具模拟多种正常&#xff0c;峰值&#xff0c;以及异常的负载情况下对系统各项性能指标进行的测试。 负载测试、压力测试、容量测试都属于性能测试。 性能测试指标是衡量系统性能的评价标准 主要关注一些响应时间、并发用户/并发、点击率、吞吐量、…

【ARMv8/ARMv9 硬件加速系列 3.2 -- SVE 读写内存指令 st1b | st1w | st1w | st1d 使用介绍】

文章目录 SVE Load 和 Store 指令使用介绍LD1 加载指令ST1 存储指令PFR 预取指令参考示例LD1 加载示例ST1 存储示例 代码实例 SVE Load 和 Store 指令使用介绍 ARMv9架构中的SVE&#xff08;Scalable Vector Extension&#xff09;指令集为向量计算提供了强大支持&#xff0c;…

10W大奖等你瓜分,OpenTiny CCF开源创新大赛报名火热启动!

OpenTiny CCF开源创新大赛正式启幕&#xff01; &#x1f31f;10万奖金&#xff0c;等你来战&#xff01; &#x1f31f; &#x1f465;无论你是独行侠还是团队英雄&#x1f465; 只要你对前端技术充满热情&#xff0c; 渴望在实战中磨砺技能&#xff0c; 那么&#xff0c…

Day 23 669. 修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树

# 669. 修剪二叉搜索树 思路 相信看到这道题目大家都感觉是一道简单题&#xff08;事实上leetcode上也标明是简单&#xff09;。 但还真的不简单&#xff01; #递归法 直接想法就是&#xff1a;递归处理&#xff0c;然后遇到 root->val < low || root->val >…

SpringCloud微服务架构(eureka、nacos、ribbon、feign、gateway等组件的详细介绍和使用)

一、微服务演变 1、单体架构&#xff08;Monolithic Architecture&#xff09; 是一种传统的软件架构模式&#xff0c;应用程序的所有功能和组件都集中在一个单一的应用中。 在单体架构中&#xff0c;应用程序通常由一个大型的、单一的代码库组成&#xff0c;其中包含了所有…

英伟达开源 3400 亿巨兽:98% 合成数据训出最强开源通用模型,性能对标 GPT-4o

NVIDIA 最近开源了其大型语言模型 Nemotron-4 340B&#xff0c;这是一个具有划时代意义的模型&#xff0c;它使用了高达 98% 的合成数据进行训练&#xff0c;并且在性能上与 GPT-4 相当。Nemotron-4 340B 包括基础模型、指令模型和奖励模型&#xff0c;支持 4K 上下文窗口、50 …

leetcode刷题记录42-1584. 连接所有点的最小费用

问题描述 给你一个points 数组&#xff0c;表示 2D 平面上的一些点&#xff0c;其中 points[i] [xi, yi] 。 连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的 曼哈顿距离 &#xff1a;|xi - xj| |yi - yj| &#xff0c;其中 |val| 表示 val 的绝对值。 请你返回将所有点连…

Linux--MQTT(一)简介

一、简介 MQTT &#xff08; Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输&#xff09;&#xff0c; 是一种基于客户端服务端架构的发布/订阅模式的消息传输协议。 与 HTTP 协议一样&#xff0c; MQTT 协议也是应用层协议&#xff0c;工作在 TCP/IP 四…

Mybatis做批量操作

动态标签foreach&#xff0c;做过批量操作&#xff0c;但是foreach只能处理记录数不多的批量操作&#xff0c;数据量大了后&#xff0c;先不说效率&#xff0c;能不能成功操作都是问题&#xff0c;所以这里讲一讲Mybatis正确的批量操作方法&#xff1a; 在获取opensession对象…

实用软件下载:XMind 2024最新安装包及详细安装教程

​XMind不仅是一款易用且功能强大的思维导图软件&#xff0c;也是一个开源项目。XMind以构建一个社区向全球提供领先的跨平台思维导图和头脑风暴软件为目标&#xff0c;以帮助用户提升效率。XMind公司是XMind开源项目的主要代码贡献者&#xff0c;与此同时&#xff0c;我们欢迎…

SpringCloud之Zuul源码解析

Zuul 是在云平台上提供动态路由&#xff0c;监控&#xff0c;弹性&#xff0c;安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。Zuul 可以适当的对多个 Amazon Auto Scaling Groups 进行路由请求。 其架构如下图所示&#xff1a; Zuu…

高速公路智能管理系统:构建安全畅通的数字大动脉

随着城市化进程的加速和交通需求的增长&#xff0c;高速公路系统作为城市交通的重要组成部分&#xff0c;正承担着越来越多的交通运输任务。为了提升高速公路的安全性、便捷性和智能化管理水平&#xff0c;高速公路智能管理系统应运而生。本文将深入探讨高速公路智能管理系统的…

Linux shell编程学习笔记58:cat /proc/mem 获取系统内存信息

0 前言 在开展系统安全检查的过程中&#xff0c;除了收集cpu信息&#xff0c;我们还需要收集内存信息。在Linux中&#xff0c;获取内存信息的命令很多&#xff0c;这里我们着重研究 cat /proc/mem命令。 1 cat /proc/mem命令 /proc/meminfo 文件提供了有关系统内存的使用情况…

能耗分析与远程抄表是什么?

一、引言 在21世纪的数字化时代&#xff0c;能耗分析和远程抄表已成为现代能源管理的重要组成部分。这两项技术不仅提高了能源效率&#xff0c;还为企业和个人提供了更精细的能源使用数据&#xff0c;从而实现更科学的节能减排。 二、能耗分析的深度洞察 能耗分析是通过收集…

[Java基本语法] 逻辑控制与方法

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;线程与…