Redis的八种数据类型介绍

Redis 是一个高性能的键值存储,它支持多种丰富的数据类型。每种数据类型都有其特定的用途和底层实现。下面我将介绍 Redis 支持的主要数据类型及其背后的数据结构。
本人这里还有几篇详细的Redis用法文章,可以用来进阶康康!

1. 字符串 (String)

数据结构

字符串是 Redis 中最基本的数据类型,可以是文本、数字、二进制数据等。它在 Redis 内部是以动态字符串 (SDS, Simple Dynamic String) 实现的。SDS 除了字符串本身,还有额外的属性来缓存长度。

  • 简单动态字符串 (SDS)
    • length:当前已使用的字符串长度。
    • free:未使用的预分配空间长度。
    • buf:实际存储字符串的缓冲区。
用法示例
Jedis jedis = new Jedis("localhost");
jedis.set("key", "value");  // 设置键值对
String value = jedis.get("key");  // 获取值
System.out.println(value);
jedis.close();

2. 哈希 (Hash)

数据结构

哈希是键值对的集合,适合表示对象的属性。最常用的是用来存储对象。内部实现主要有两种:

  • 压缩列表 (ziplist):当哈希数量较少且每个键值对的长度较短时会使用。
  • 哈希表 (hashtable):当哈希数量增多或其中某个键或值变长后,自动转为哈希表。
用法示例
Jedis jedis = new Jedis("localhost");
jedis.hset("user:1000", "name", "John");
jedis.hset("user:1000", "age", "30");
String name = jedis.hget("user:1000", "name");
System.out.println(name);
jedis.close();

3. 列表 (List)

数据结构

列表是一组有序的字符串,可以从列表的头部或尾部添加或删除元素。内部实现有以下两种:

  • 压缩列表 (ziplist):元素数量较少且每个元素较短时使用。
  • 双向链表 (linkedlist):元素数量多时,会自动转为双向链表。
用法示例
Jedis jedis = new Jedis("localhost");
jedis.lpush("tasks", "Task1");  // 从头部插入
jedis.rpush("tasks", "Task2");  // 从尾部插入
String task = jedis.lpop("tasks");  // 从头部弹出
System.out.println(task);
jedis.close();

4. 集合 (Set)

数据结构

集合是无序且唯一的字符串集合。内部采用哈希表 (hashtable) 实现,哈希表的键是集合的值,值是 null。

用法示例
Jedis jedis = new Jedis("localhost");
jedis.sadd("tags", "java");
jedis.sadd("tags", "redis");
jedis.sadd("tags", "java");  // 不会重复添加
Set<String> tags = jedis.smembers("tags");
System.out.println(tags);
jedis.close();

5. 有序集合 (Sorted Set)

数据结构

有序集合类似集合,但每个元素都会关联一个分数(score),元素按分数排序。内部使用一种结构:跳表 (skiplist) 和哈希表 (hashtable) 结合。

  • 跳表 (skiplist):快速实现范围查询和排序。
  • 哈希表 (hashtable):快速定位元素。
用法示例
Jedis jedis = new Jedis("localhost");
jedis.zadd("leaderboard", 100, "Player1");
jedis.zadd("leaderboard", 200, "Player2");
Set<String> topPlayers = jedis.zrange("leaderboard", 0, -1);  // 按分数排序获取元素
System.out.println(topPlayers);
jedis.close();

6. 位图 (Bitmap)

数据结构

位图将字符串值视为一个位数组,可以进行位操作。内部存储采用字符串,最大长度可达 512 MB。

  • 字节数组 (byte array):实际存储位图数据的结构,是一个连续的内存区域。
用法示例
Jedis jedis = new Jedis("localhost");
jedis.setbit("user:active", 1, true);  // 设置第2位为1
boolean isActive = jedis.getbit("user:active", 1);
System.out.println(isActive);
jedis.close();

7. HyperLogLog

数据结构

HyperLogLog 是基数估算数据结构,用于估算不重复元素的数量。利用概率算法实现,误差率约 0.81%。用于大规模数据去重计数。

  • 稀疏矩阵 (Sparse Representation):当元素少时,用压缩方法存储。
  • 密集矩阵 (Dense Representation):当元素多到一定程度时,转为密集表示。
用法示例
Jedis jedis = new Jedis("localhost");
jedis.pfadd("unique_visitors", "user1");
jedis.pfadd("unique_visitors", "user2");
jedis.pfadd("unique_visitors", "user1");
long uvCount = jedis.pfcount("unique_visitors");
System.out.println(uvCount);
jedis.close();

8. 地理空间 (Geospatial)

数据结构

Redis 的地理空间扩展允许存储地理坐标,并提供地理范围查询、距离计算等功能。内部使用有序集合 (Sorted Set) 数据类型实现,利用 GeoHash 编码。

  • GeoHash 编码 (geohash):将地理坐标编码为字符串,保证地理位置靠近的两个编码也是靠近的。
  • 跳表 (skiplist) 和哈希表 (hashtable):与有序集合共享的实现,支持半径查询和排序。
用法示例
Jedis jedis = new Jedis("localhost");
jedis.geoadd("locations", 13.361389, 38.115556, "Palermo");
jedis.geoadd("locations", 15.087269, 37.502669, "Catania");List<GeoCoordinate> coordinates = jedis.geopos("locations", "Palermo");
System.out.println(coordinates);double distance = jedis.geodist("locations", "Palermo", "Catania", GeoUnit.KM);
System.out.println(distance);
jedis.close();

结论

Redis 提供了丰富的数据结构,每种数据结构都有其特定的应用场景和优势。在实践中,可以根据具体需求选择合适的数据类型,提高系统性能与效率。了解这些数据结构的底层实现有助于更好地理解和使用 Redis,优化数据存储和操作。

另(面试突出加分项):
跳表(SkipList)是一种用于有序集合的高效数据结构,支持快速的插入、删除和查找操作。它的时间复杂度为 O(log N),接近于平衡二叉树,但实现相对简单。跳表是 Redis 中 Sorted Set(有序集合)数据类型的主要底层实现之一。

在这里插入图片描述

跳表的数据结构

跳表由多层有序链表组成,下层链表包含所有元素,每一层链表是其下一层链表的抽样。跳表的最底层是一条完整的有序链表,从头到尾包含所有的元素。每一层往上减少一部分元素,通过跳过一些元素使操作更高效。

  • Level 0:包含所有节点,构成最底层链表。
  • Level 1:包含部分节点,跳过一些元素,形成较高层次的链表。
  • Level k:最高层链表,包含最少的元素。

跳表的节点

每个节点包含多个指针,每个指针指向该层中的下一个节点。节点结构如下:

  • score:用于排序的分数。
  • member:实际存储的值。
  • forward pointers:指向各层下一个节点的指针数组。

跳表的操作

跳表支持插入、删除和查找操作,具体步骤如下:

插入操作
  1. 从最高层开始,找到要插入的位置。
  2. 随机生成节点的高度(层数)。
  3. 将新节点插入到对应的每一层链表中。
删除操作
  1. 从最高层开始,找到要删除的节点。
  2. 更新指针,跳过该节点。
  3. 逐层删除该节点。
查找操作
  1. 从最高层开始,根据分数找到目标位置。
  2. 如果当前层找不到,降到下一层继续查找。
  3. 直到找到目标节点或确认节点不存在。

跳表在 Redis 中的实现

以下是 Redis 跳表的数据结构和主要操作的实现:

节点结构
typedef struct zskiplistNode {struct zrobj *obj; // 存储成员对象(value)double score;      // 分数,用于排序struct zskiplistNode *backward; // 后退指针struct zskiplistLevel {struct zskiplistNode *forward; // 前进指针unsigned int span;             // 跨度} level[]; // 层指针数组
} zskiplistNode;
跳表结构
typedef struct zskiplist {struct zskiplistNode *header, *tail; // 头尾指针unsigned long length;                // 节点数量int level;                           // 当前最大层数
} zskiplist;
插入操作
zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj) {// 临时存储每一层的前一个节点zskiplistNode *update[ZSKIPLIST_MAXLEVEL];zskiplistNode *x;int i, level;x = zsl->header;for (i = zsl->level-1; i >= 0; i--) {while (x->level[i].forward && (x->level[i].forward->score < score ||(x->level[i].forward->score == score &&compareStringObjects(x->level[i].forward->obj, obj) < 0))) {x = x->level[i].forward;}update[i] = x;}level = zslRandomLevel(); // 随机生成节点层数if (level > zsl->level) {for (i = zsl->level; i < level; i++) {update[i] = zsl->header;}zsl->level = level;}x = zslCreateNode(level, score, obj); // 创建新节点for (i = 0; i < level; i++) {x->level[i].forward = update[i]->level[i].forward;update[i]->level[i].forward = x;}x->backward = (update[0] == zsl->header ? NULL : update[0]);if (x->level[0].forward)x->level[0].forward->backward = x;elsezsl->tail = x;zsl->length++;return x;
}
删除操作
int zslDelete(zskiplist *zsl, double score, robj *obj) {zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;int i;x = zsl->header;for (i = zsl->level-1; i >= 0; i--) {while (x->level[i].forward && (x->level[i].forward->score < score ||(x->level[i].forward->score == score &&compareStringObjects(x->level[i].forward->obj, obj) < 0))) {x = x->level[i].forward;}update[i] = x;}x = x->level[0].forward;if (x && score == x->score && compareStringObjects(x->obj, obj) == 0) {for (i = 0; i < zsl->level; i++) {if (update[i]->level[i].forward == x) {update[i]->level[i].forward = x->level[i].forward;}}if (x->level[0].forward) {x->level[0].forward->backward = x->backward;} else {zsl->tail = x->backward;}while (zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL) {zsl->level--;}zsl->length--;zslFreeNode(x);return 1;}return 0;
}
查找操作
zskiplistNode *zslFind(zskiplist *zsl, double score, robj *obj) {zskiplistNode *x = zsl->header;int i;for (i = zsl->level-1; i >= 0; i--) {while (x->level[i].forward && (x->level[i].forward->score < score ||(x->level[i].forward->score == score &&compareStringObjects(x->level[i].forward->obj, obj) < 0))) {x = x->level[i].forward;}}x = x->level[0].forward;if (x && score == x->score && compareStringObjects(x->obj, obj) == 0) {return x;}return NULL;
}

跳表的优点

  1. 高效性:查找、插入、删除的平均时间复杂度为 O(log N)。
  2. 简单性:相较于平衡树,跳表的实现和维护更为简单。
  3. 灵活性:能够快速应对动态变化的数据,有较好的适应性。

在 Redis 中的应用

跳表主要用于 Redis 的有序集合 (Sorted Set) 数据类型。通过跳表,可以高效实现按分数排序的多种操作,如范围查询、排名查询等。它与哈希表结合使用,实现了元素的快速访问和分数范围内的高效查询。

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

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

相关文章

轻松跨越国界:使用WildCard畅享全球AI服务

大家好&#xff0c;现在AI技术已经深入到我们的日常生活中。然而&#xff0c;许多朋友仍然难以获取优质的AI工具和应用。那么&#xff0c;如何才能使用像ChatGPT这样的AI服务呢&#xff1f; 今天我为大家介绍一个“一劳永逸”的解决方案&#xff0c;它就是我们的主角——WildC…

基于antv x6实现的组织架构图

X6 是基于 HTML 和 SVG 的图编辑引擎&#xff0c;基于 MVC 架构&#xff0c;用户更加专注于数据逻辑和业务逻辑。 一、业务背景 将组织树形结构图形化&#xff0c;更直观的展示个人所在的组织架构。 二、功能点 组织结构按需渲染&#xff0c;支持层级展开、收缩按需求自定义…

秋招突击——设计模式补充——单例模式、依赖倒转原则、工厂方法模式

文章目录 引言正文依赖倒转原则工厂方法模式工厂模式的实现简单工厂和工厂方法的对比 抽线工厂模式最基本的数据访问程序使用工厂模式实现数据库的访问使用抽象工厂模式的数据访问程序抽象工厂模式的优点和缺点使用反射抽象工厂的数据访问程序使用反射配置文件实现数据访问程序…

PhysioLLM 个性化健康洞察:手表可穿戴设备实时数据 + 大模型

个性化健康洞察&#xff1a;可穿戴设备实时数据 大模型 提出背景PhysioLLM 图PhysioLLM 实现数据准备用户模型和洞察生成个性化数据总结和洞察是如何生成的&#xff1f; 解析分析 提出背景 论文&#xff1a;https://arxiv.org/pdf/2406.19283 虽然当前的可穿戴设备伴随应用&…

S272钡铼技术4G无线RTU支持多路DIN输入和模拟量转换至4G网络

钡铼第四代RTU S272是一款先进的工业级4G远程遥测终端&#xff0c;为各种远程工业数据采集和控制系统提供了高效解决方案。结合了现代通信技术和多功能的输入输出接口&#xff0c;S272不仅支持多路数字量和模拟量输入&#xff0c;还具备灵活的扩展性和强大的控制功能&#xff0…

什么是嵌入式,单片机又是什么,两者有什么关联又有什么区别?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;从科普的角度&#xff0c;…

Python酷库之旅-第三方库Pandas(001)

目录 一、Pandas库的由来 1、背景与起源 1-1、开发背景 1-2、起源时间 2、名称由来 3、发展历程 4、功能与特点 4-1、数据结构 4-2、数据处理能力 5、影响与地位 5-1、数据分析“三剑客”之一 5-2、社区支持 二、Pandas库的应用场景 1、数据分析 2、数据清洗 3…

记录OSPF配置,建立邻居失败的过程

1.配置完ospf后&#xff0c;在路由表中不出现ospf相关信息 [SW2]ospf [SW2-ospf-1]are [SW2-ospf-1]area 0 [SW2-ospf-1-area-0.0.0.0]net [SW2-ospf-1-area-0.0.0.0]network 0.0.0.0 Jul 4 2024 22:11:58-08:00 SW2 DS/4/DATASYNC_CFGCHANGE:OID 1.3.6.1.4.1.2011.5.25 .1…

二分查找及其变种

一、概念 二分查找算法&#xff08;Binary Search Algorithm&#xff09;是一种在有序数组中查找特定元素的高效搜索方法。 其基本思想是将目标值与数组中间的元素进行比较&#xff0c;如果目标值等于中间元素&#xff0c;则查找成功&#xff1b;如果目标值小于中间元素&…

Visual Studio 设置回车代码补全

工具 -> 选项 -> 文本编辑器 -> C/C -> 高级 -> 主动提交成员列表 设置为TRUE

使用EndNote在Word中插入参考文献,并编辑参考文献样式方法

一、背景 在准备中期报告时&#xff0c;学校给的是Word模板&#xff0c;习惯了Latex排版和添加参考文献的便利后&#xff0c;真不想用word写东西。 之前投《机器人》期刊&#xff08;被拒了&#xff09;和准备开题的时候也是用word写的&#xff0c;当时为方便添加参考文献和定…

【人工智能】--生成对抗网络

个人主页&#xff1a;欢迎来到 Papicatch的博客 课设专栏 &#xff1a;学生成绩管理系统 专业知识专栏&#xff1a; 专业知识 文章目录 &#x1f349;引言 &#x1f349;GAN 的基本原理 &#x1f348;生成器&#xff08;Generator&#xff09; &#x1f348;判别器&…

【嵌入式DIY实例-ESP8266篇】-LCD ST7735显示DS3231 RTC时间

LCD ST7735显示DS3231 RTC时间 文章目录 LCD ST7735显示DS3231 RTC时间1、硬件准备与接线2、代码实现本文将介绍如何使用 ESP8266 NodeMCU 板 (ESP-12E) 和 DS3231 RTC 模块制作一个简单的数字实时时钟,其中可以使用连接到 NodeMCU 的两个按钮设置时间和日期,并将它们打印(带…

llm学习-4(llm和langchain)

langchain说明文档&#xff1a;langchain 0.2.6 — &#x1f99c;&#x1f517; langChain 0.2.6https://api.python.langchain.com/en/latest/langchain_api_reference.html#module-langchain.chat_models 1&#xff1a;模型 &#xff08;1&#xff09;自定义模型导入&#x…

c语言回顾-内存操作函数

目录 前言 1.memcpy 函数 1.1函数介绍 1.2与strcpy的区别 1.3memcpy的模拟 2.memmove 函数 2.1函数介绍和使用 2.2函数的模拟 3.memset函数 3.1函数介绍 3.2函数的模拟 4.memcmp函数 4.1函数的使用 4.2函数的模拟 结束语 前言 在动态内存的章节中小编详细讲解了动…

7.基于SpringBoot的SSMP整合案例-表现层开发

目录 1.基于Restfu1进行表现层接口开发 1.1创建功能类 1.2基于Restful制作表现层接口 2.接收参数 2使用Apifox测试表现层接口功能 保存接口&#xff1a; 分页接口&#xff1a; 3.表现层一致性处理 3.1先创建一个工具类&#xff0c;用作后端返回格式统一类&#xff1a;…

【人工智能学习之图像操作(一)】

【人工智能学习之图像操作&#xff08;一&#xff09;】 图像读写创建图片并保存视频读取色彩空间与转换色彩空间的转换通道分离理解HSV基本图形绘制 阀值操作OTSU二值化简单阀值自适应阀值 图像读写 图像的读取、显示与保存 import cv2 img cv2.imread(r"1.jpg")…

Unity休闲手机游戏开发课程

课程介绍 Unity休闲手机游戏开发课程将教您如何利用Unity游戏引擎创建令人愉快的休闲手机游戏。从基础的游戏开发知识到高级的游戏制作技巧&#xff0c;您将学习到创建各种类型的休闲游戏所需的关键技能和工具。无论您是初学者还是有一定经验的开发者&#xff0c;本课程都能帮助…

协程调度模块

什么是协程和协程调度&#xff1f; 基本概念 协程 协程是一种比线程更轻量级的并发编程结构&#xff0c;它允许在函数执行过程中暂停和恢复执行状态&#xff0c;从而实现非阻塞式编程。协程又被称为用户级线程&#xff0c;这是由于协程包括上下文切换在内的全部执行逻辑都是…

[linux]sed命令基础入门详解

sed是一种流编辑器&#xff0c;它一次处理一行内容。处理时&#xff0c;把当前处理的行存储在临时缓冲区中&#xff0c;称为“模式空间”&#xff0c;接着用sed命令处理缓冲区中的内容&#xff0c;处理完成后&#xff0c;把缓冲区的内容送往屏幕。接着处理下一行&#xff0c;这…