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.mzph.cn/bicheng/40243.shtml

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

相关文章

ubuntu24.04LTS防火墙设置

Ubuntu24.04LTS开箱自带ufw&#xff0c;一定程度避免了开机下载ufw被攻击&#xff0c;excellent 转载aliyun教程 sudo ufw enbale可以启用并且开机自启(显示有效&#xff0c;未nmap实测) 教程3 转载自CSDN 完整格式如下&#xff1a; # 禁止IP连接端口 sudo ufw deny proto tc…

Cherno 游戏引擎笔记 (45~60)

有几个部分的笔记以图片形式呈现&#xff08;如果没找到文本可以查看是否遗漏了图片笔记&#xff09; My Github REPO(GitHub - JJJJJJJustin/Nut: The game_engine which learned from Cherno) 源码笔记&#xff0c;希望帮到你 :-} ---Shader Library&#xff08;着色器库&…

南京观海微电子----AC/DC、DC/DC转换器知识

什么是AC&#xff1f; Alternating Current&#xff08;交流&#xff09;的首字母缩写。 AC是大小和极性&#xff08;方向&#xff09;随时间呈周期性变化的电流。 电流极性在1秒内的变化次数被称为频率&#xff0c;以Hz为单位表示。 什么是DC? Direct Current&#xff08;直流…

visual studio远程调试

场景一&#xff08;被远程调试的电脑&#xff09; 确定系统位数 我这里是x64的 找到msvsmon.exe msvsmon.exe目录位置解释&#xff1a; “F:\App\VisualStudio\an\Common7\IDE\”是visual studio所在位置、 “Remote Debugger\”是固定位置、 “x64”是系统位数。 拼起来就是…

grid布局下的展开/收缩过渡效果【vue/已验证可正常运行】

代码来自GPT4o&#xff1a;国内官方直连GPT4o <template><div class"container"><button class"butns" click"toggleShowMore">{{ showAll ? 收回 : 显示更多 }}</button><transition-group name"slide-fade&…

数据库原理实验报告第二次-SQL Server SSMS工具创建和管理数据库及数据表.

题目 1、使用SSMS工具创建名为ecommerce的数据库&#xff0c;并查看或修改数据库属性 2、在数据库ecommerce中创建如下表&#xff1a; &#xff08;1&#xff09;商品类别表category 字段名 数据类型 允许NULL值 约束 字段说明 catno int 否 主键 商品类别编号 ca…

AI是在帮助开发者还是取代他们?

一&#xff1a;介绍 生成式人工智能&#xff08;AIGC&#xff09;在软件开发领域的应用确实为开发者带来了很多便利和效率提升。AI工具可以通过代码生成、错误检测、自动化测试等功能&#xff0c;帮助开发者更快速地开发和优化软件&#xff0c;减少重复性工作&#xff0c;提高…

哈喽GPT-4o,对GPT-4o 论文速写的思考与探索

作为一款强大的语言模型&#xff0c;ChatGPT 在论文写作上具备显著优势。它能够辅助学者或研究人员自动创建论文框架、摘要、文献综述及论文段落&#xff08;如引言、方法、结果、结论等&#xff09;。此外&#xff0c;ChatGPT 还能优化论文结构、润色、降低内容重复率&#xf…

比Proxmox VE更易用的免费虚拟化平台

之前虚拟化一直玩Proxmox VE&#xff0c;最近发现一个更易用的虚拟化软件CSYun&#xff0c;他与Proxmox VE类似&#xff0c;都是一个服务器虚拟化平台。它不像VMware ESXi那么复杂&#xff0c;对于个人使用者和中小企业是一个比较好的选择。 这个软件所在的网址为&#xff1a;…

【Python】已解决TypeError: init() got an unexpected keyword argument ‘threshold’

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决TypeError: init() got an unexpected keyword argument ‘threshold’ 一、分析问题背景 在Python编程中&#xff0c;遇到“TypeError: init() got an unexpected keyword …

Three.js机器人与星系动态场景(二):强化三维空间认识

在上篇博客中介绍了如何快速利用react搭建three.js平台&#xff0c;并实现3D模型的可视化。本文将在上一篇的基础上强化坐标系的概念。引入AxesHelper辅助工具及文本绘制工具&#xff0c;带你快速理解camer、坐标系、position、可视区域。 Three.js机器人与星系动态场景&#x…

色彩搭配的艺术:打造和谐视觉体验的秘诀

当设计作品呈现给用户时首先映入眼帘的是视觉表达&#xff0c;色彩无疑是最关键的元素之一。色彩不仅是视觉艺术的一部分&#xff0c;也承载着情感文化甚至个人品味的多重含义。在设计领域&#xff0c;色彩设计可以极大地影响作品的整体感受和传达效果。那么什么是色彩设计&…

python拉取gitlab项目以及拉取报错处理

问题解决 问题1、unable to access https://gitlab.dome.com/web-dome/dome.git/: SSL certificate problem: self signed certificate 解决&#xff1a;打开本地git bash输入下面代码 git config --global http.sslVerify false; 问题2、Authentication failed for https:…

ArmPiPro-网络配置

说明 因为ubuntu18.04有点旧&#xff0c;这里同时用上了netplan和nm(nmcli)这两个网络工具&#xff0c;如果是ubuntu22.04&#xff0c;网络管理全部用nmcli和nmtui即可。 eth0有线一般用来连接电脑&#xff0c;wlan0即是用来连接WiFi热点。 设置Pi4Lubuntu的Wifi模式&#xff…

ComboBox控件如果有两个相同的项,并且你选择了第二个,再次打开下拉列表时,它可能不会定位到你选择的上面第一个项问题

一、MFC的ComBox绑定项,有两个项名称相同,选择第二个绑定项,再次打开下拉框会定位到第 在MFC中&#xff0c;ComboBox控件默认会在选择项改变时发送CBN_SELCHANGE通知&#xff0c;但如果ComboBox中有两个相同的项&#xff0c;并且你选择了第二个&#xff0c;再次打开下拉列表时…

CSS动画keyframes简单样例

一、代码部分 1.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><link rel"stylesheet" href…

数据泄露时代的安全之道:访问认证的重要性

引言 想象一下&#xff1a;你一觉醒来&#xff0c;收到一条通知——你的公司遭遇了数据泄露。你感到恐惧&#xff0c;因为这意味着客户数据被曝光&#xff0c;公司声誉受损&#xff0c;还有巨额罚款在等着你。在当今的数字化环境中&#xff0c;这种情况太常见了。全球各地的组…

【分布式系统】监控平台Zabbix自定义模版配置

目录 一.添加Zabbix客户端主机 1.服务端跟客户端配置时间同步 2.安装 zabbix-agent2 3.修改 agent2 配置文件 4.服务端安装 zabbix-get验证客户端数据的连通性 5.Web 页面中添加 agent 主机 6.监控模板 二.自定义监控内容 1.客户端创建自定义key 1.1.明确需要执行的 …

优先级队列(堆)学的好,头发掉的少(Java版)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

问界3D-为什么在建模的过程中要先映射再展开呢

问题提出&#xff1a;为什么在建模的过程中要先映射再展开呢 在建模过程中先进行映射再展开的原因主要涉及到工作流程的效率和纹理质量的保证。具体来说&#xff0c;以下几点解释了为什么这一顺序是合理且常见的&#xff1a; 1. 初步确定UV布局 属 性描述映射通过不同类型的映…