Redis数据库(二):Redis 常用的五种数据结构
Redis 能够做到高性能的原因主要有两个,一是它本身是内存型数据库,二是采用了多种适用于不同场景的底层数据结构。
Redis 常用的数据结构支持字符串、列表、哈希表、集合和有序集合。实现这些数据结构的底层数据结构有 6 种,分别是简单动态字符串、双向列表、压缩列表、哈希表、跳表和整数数组。

List、 Hash、Set 和 Sorted Set 这四种数据类型,都有两种底层实现结构。通常情况下,我们会 把这四种类型称为集合类型,它们的特点是一个键对应了一个集合的数据。
那么,有一些问题值得我们去思考:
- 既然 Redis 是键值型数据结构,那么键和值本身之间用什么结构组织?
- 操作集合数据的效率和哪些因素有关?
2.1 键和值用什么数据结构组织?
为了实现从键到值的快速访问,Redis 使用了一个哈希表来保存所有键值对。一个哈希表,其实就是一个数组,数组的每个元素称为一个哈希桶。
因为值本身的类型可以是列表、哈希表等集合类型,因此哈希桶存放的并不是值本身,而是 *key 和 *value 的入口地址。

使用哈希表的好处就是能够在 O(1) 时间内根据 key 查找到相应的 value。但,哈希表的性能并非一直是 O(1)。当需要解决哈希表冲突和再哈希(rehash)时可能会带来性能的下降。
2.1.1 哈希表冲突
哈希表冲突是指根据不同的 key 计算得到了相同的哈希值,就是说有不同的键值对放到了同一个哈希桶当中。因为哈希桶的个数是有限的,因此总会遇到哈希冲突。
解决哈希冲突的方法有多种,Redis 采用的是链式哈希。具体来说,就是同一个哈希桶中的多个元素用一个链表来保存,它们之间通过指针来连接。

链式哈希会带一个问题是,当位于同一个桶中的元素太多时,查询桶中元素的时间复杂度会退化到 O(n),这是我们不愿看到的。解决办法就是对旧的哈希表进行 rehash,将新的哈希值存放在一个更大的哈希桶中。但如果直接进行原地哈希,必然会导致性能急剧下降,解决的办法就是将 rehash 的操作均摊到之后的操作中。
2.1.2 渐进式 rehash
渐进式 rehash 的思想是在拷贝数据时,Redis 仍然正常处理客户端请求,每处理一个请求 时,从哈希表 1 中的第一个索引位置开始,顺带着将这个索引位置上的所有 entries 拷贝 到哈希表 2 中;等处理下一个请求时,再顺带拷贝哈希表 1 中的下一个索引位置的 entries。

这样就巧妙地把一次性大量拷贝的开销,分摊到了多次处理请求的过程中,避免了耗时操作,保证了数据的快速访问。
2.2 操作数据集合的效率
查找一个集合类型的值的过程是:先通过全局哈希表找到对应的哈希桶位置,然后在集合进行增删改查。那么,集合的操作效率与哪些因素有关呢?
首先是与集合底层采用的数据结构有关,例如哈希表的查询时间复杂度要优于链表的。其次是与这些操作本身的执行特点有关,例如读取一个元素和读取一个范围的元素。
2.2.1 底层数据结构的特点
Redis 实现集合类型的底层数据结构有双向列表、整数数组、哈希表、压缩列表和跳表。
哈希表的特点刚才已经介绍过了。双向列表和整数数组比较常见,这里就不再详细展开了。
重点介绍一个压缩列表和跳表。
压缩列表
压缩列表类似于一个数组,数组中的每一个元素都对应保存一个数据。和数组不同的是,压缩列表在表头有三个字段 zlbytes、zltail 和 zllen,分别表示列表长度、列表尾的 偏移量和列表中的 entry 个数;压缩列表在表尾还有一个 zlend,表示列表结束。

在压缩列表中,如果我们要查找定位第一个元素和最后一个元素,可以通过表头三个字段的长度直接定位,复杂度是 O(1)。而查找其他元素时,就没有这么高效了,只能逐个查找,此时的复杂度就是 O(N) 了。
跳表
跳表在链表的基础上,增加了多级索引,通过索引位置的几个跳转,实现数据的快速定位,跳表的查询过程如下图所示:

以上五种数据结构的时间复杂度如下表所示:

2.2.2 不同操作的复杂度
集合类型的操作方法很多,例如获取单个元素、多个元素、判断某个元素是否在集合当中。而不同数据结构的同一种操作方法的时间复杂度不同,因此有必要了解不同操作方法的时间复杂度。
- 单元素操作时间复杂度为 O(1);
- 范围操作比较耗时。当返回一个范围内的元素时,例如返回集合、List 某个范围的元素,时间复杂度为 O(N)。
- 统计元素个数比较高效。压缩列表、双向列表、整数数组这些数据结构中专门记录了元素的个数,统计执行效率较高。
- 特殊位置的元素操作效率与数据结构密切相关。例如对于 List 而言,在头尾进行操作元素的时间复杂度为 O(1),而在中间位置操作元素的时间复杂度为 O(N)。
2.3 使用数据结构的建议
选择 Redis 的数据结构时,需要综合考虑数据的特点、操作需求、内存占用和性能要求等多个因素。
选择 Redis 数据结构时,应该考虑以下几个因素:
-
数据访问模式
• 字符串(String):适用于简单的 key-value 存储,比如缓存用户会话、计数器、或是简单的数据存取。
• 哈希(Hash):适合存储对象、结构化数据,如用户信息、商品详情等。哈希允许在单一 key 下保存多个字段(字段值对),非常适合于存储较小的对象。
• 列表(List):适合存储有序的集合,比如消息队列、任务队列、用户操作日志等。支持推入、弹出操作,符合生产者-消费者模型。
• 集合(Set):适合存储无序且唯一的元素集合,用于去重操作、标签分类等。例如,存储用户订阅的标签列表,或是某些“推荐系统”的推荐项。
• 有序集合(Sorted Set):适合存储带有权重的有序数据,支持根据分数进行排序。适用于排行榜、优先级队列、延迟队列等应用场景。 -
性能要求
• 存储效率:不同数据结构的存储方式和空间效率不同。比如,集合(Set)适合去重,哈希(Hash)适合存储对象,如果需要存储大量字段和小值数据,使用哈希结构可以节省内存。
• 操作效率:不同数据结构在操作时的复杂度也不同。一般来说,字符串操作最简单,复杂度为 O(1),而有序集合(Sorted Set)的某些操作,如添加、删除元素的复杂度可能为 O(log N)。 -
数据大小与访问频率
• 如果需要频繁对数据进行增、删、改、查等操作,选择操作复杂度较低的数据结构,例如字符串和哈希。
• 对于大数据量和高并发的场景,考虑 Redis 的内存消耗和性能瓶颈。比如,HyperLogLog 在大数据量去重时非常高效。 -
使用场景
• 缓存:大多数缓存场景使用字符串(String)或哈希(Hash)。例如,缓存一整个页面或用户信息。
• 队列:使用列表(List)或有序集合(Sorted Set)来实现消息队列、任务调度等。
• 去重与标签:集合(Set)适用于去重和标签管理。
• 排行榜与优先级队列:使用有序集合(Sorted Set)存储带权重的数据,并根据分数进行排序。 -
数据过期与持久化
• Redis 提供了 EXPIRE 命令来为 key 设置过期时间,但不同数据结构的持久化策略可能不同。根据业务需求决定是否需要启用 Redis 的持久化机制(RDB 或 AOF),以及数据结构的持久化需求。
总结来说,选择 Redis 数据结构的关键是结合具体的业务需求、访问模式、性能要求等因素来进行权衡。在有些情况下,可能还需要多种数据结构的组合使用,以达到最佳效果。
实际上,在 Redis 7.4 版本中,已经支持 9 种数据结构,分别是String、Hash、List、Set、Sorted set、Stream、Bitmap、Bitfield以及Geospatial。后四种数据结构会专门再写一篇文章进行介绍。
觉得有用可以点个赞。
相关文章:
Redis数据库(二):Redis 常用的五种数据结构
Redis 能够做到高性能的原因主要有两个,一是它本身是内存型数据库,二是采用了多种适用于不同场景的底层数据结构。 Redis 常用的数据结构支持字符串、列表、哈希表、集合和有序集合。实现这些数据结构的底层数据结构有 6 种,分别是简单动态字…...
【计组】实验五 J型指令设计实验
目录 一、实验目的 二、实验环境 三、实验原理 四、实验任务 代码 一、实验目的 1. 理解MIPS处理器指令格式及功能。 2. 掌握lw, sw, beq, bne, lui, j, jal指令格式与功能。 3. 掌握ModelSim和ISE\Vivado工具软件。 4. 掌握基本的测试代码编写和FPGA开发板使用方法。 …...
ubuntu 本地部署deepseek r1 蒸馏模型
本文中的文件路径或网络代理需要根据自身环境自行删改 一、交互式chat页面 1.1 open-webui 交互窗口部署:基于docker安装,且支持联网搜索 Open WebUI 是一个可扩展、功能丰富且用户友好的自托管 AI 平台,旨在完全离线操作。它支持各种 LLM…...
RestTemplate Https 证书访问错误
错误信息 resttemplate I/O error on GET request for “https://21.24.6.6:9443/authn-api/v5/oauth/token”: java.security.cert.CertificateException: No subject alternative names present; nested exception is javax.net.ssl.SSLHandshakeException: java.security.c…...
MySQL内存使用率高且不释放问题排查与总结
背景 生产环境mysql 5.7内存占用超过90%以上,且一直下不来。截图如下: 原因分析 1、确定mysql具体的占用内存大小,通过命令:cat /proc/Mysql进程ID/status查看 命令执行后的结果比较多(其他参数的含义想了解可参考这…...
mysql8 从C++源码角度看sql生成抽象语法树
在 MySQL 8 的 C 源码中,SQL 语句的解析过程涉及多个步骤,包括词法分析、语法分析和抽象语法树(AST)的生成。以下是详细的解析过程和相关组件的描述: 1. 词法分析器(Lexer) MySQL 使用一个称为…...
【DeepSeek】DeepSeek概述 | 本地部署deepseek
目录 1 -> 概述 1.1 -> 技术特点 1.2 -> 模型发布 1.3 -> 应用领域 1.4 -> 优势与影响 2 -> 本地部署 2.1 -> 安装ollama 2.2 -> 部署deepseek-r1模型 1 -> 概述 DeepSeek是由中国的深度求索公司开发的一系列人工智能模型,以其…...
【C++】多态原理剖析
目录 1.虚表指针与虚表 2.多态原理剖析 1.虚表指针与虚表 🍪类的大小计算规则 一个类的大小,实际就是该类中成员变量之和,需要注意内存对齐空类:编译器给空类一个字节来唯一标识这个类的对象 对于下面的Base类,它的…...
【Rust自学】20.4. 结语:Rust学习一阶段完成+附录
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 20.4.1. 总结 Rust初级学习之旅终于完成了!恭喜! 包括这篇文章,我们使用了110篇文章来学习Rust。 真…...
pytorch引用halcon写数据集
****加粗样式虽然啰嗦一点,但好歹halcon自己熟悉,不会忘记,用os 和 pil会导致脑子记得东西太多 import halcon as ha import torch from torch.utils.data import Datasetpath0 rE:\BaiduNetdiskDownload\cell class MyDataset(Dataset):de…...
让文物“活”起来,以3D数字化技术传承文物历史文化!
文物,作为不可再生的宝贵资源,其任何毁损都是无法逆转的损失。然而,当前文物保护与修复领域仍大量依赖传统技术,同时,文物管理机构和专业团队的力量相对薄弱,亟需引入数字化管理手段以应对挑战。 积木易搭…...
aarch64 Ubuntu20.04 安装docker
安装 docker 依赖项:sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release添加 Docker GPG 密钥:curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyr…...
JAVA:CloseableHttpClient 进行 HTTP 请求的技术指南
1、简述 CloseableHttpClient 是 Apache HttpComponents 提供的一个强大 HTTP 客户端库。它允许 Java 程序与 HTTP/HTTPS 服务交互,可以发送 GET、POST 等各种请求类型,并处理响应。该库广泛用于 REST API 调用、文件上传和下载等场景。 2、特性 Close…...
Mac上搭建k8s环境——Minikube
1、在mac上安装Minikube可执行程序 brew cask install minikub 安装后使用minikube version命令查看版本 2、安装docker环境 brew install --cask --appdir/Applications docker #安装docker open -a Docker #启动docker 3、安装kubectl curl -LO https://storage.g…...
经典排序算法复习----C语言
经典排序算法复习 分类 交换类 冒泡快排 分配类 计数排序基数排序 选择类 选择排序 堆排序 归并类 归并排序 插入类 直接插入排序 希尔排序 折半插入排序 冒泡排序 基于交换。每一轮找最大值放到数组尾部 //冒泡排序 void bubSort(int* arr,int size){bool sorte…...
自动驾驶数据集三剑客:nuScenes、nuImages 与 nuPlan 的技术矩阵与生态协同
目录 1、引言 2、主要内容 2.1、定位对比:感知与规划的全维覆盖 2.2、数据与技术特性对比 2.3、技术协同:构建全栈研发生态 2.4、应用场景与评估体系 2.5、总结与展望 3、参考文献 1、引言 随着自动驾驶技术向全栈化迈进,Motional 团…...
[LUA ERROR] bad light userdata pointer
Cocos2d项目,targetSdkVersion30,在 android 13 设备运行报错: [LUA ERROR] bad light userdata pointer ,导致黑屏。 参考 cocos2dx 适配64位 arm64-v8a 30 lua 提示 bad light userdata pointer 黑屏-CSDN博客的方法 下载最新的Cocos2dx …...
【Java八股】JVM
JVM 1. jvm内存区域分为哪些部分 线程私有的:程序计数器、虚拟机栈、本地方法栈 程序计数器:指示当前线程执行到的字节码文件的行号,是线程切换后保证线程能恢复到正确的执行位置的关键 虚拟机栈:用于存储方法调用的数据&…...
集成学习(一):从理论到实战(附代码)
一、引言 在机器学习领域,打造一个独立、强大的算法是解决问题的关键。然而,集成学习提供了一种不同的视角:通过组合多个“弱”学习器来创建一个更强大的模型。本文探讨集成学习的思想、方法及其应用。 二、机器学习 vs 集成学习思想 传统…...
Netty:高性能网络应用框架的深度解析
引言 Netty 是由 JBoss 提供的一个开源的 Java NIO 客户端/服务器框架,它用以快速开发网络应用程序,如协议服务器和客户端。它的设计目标是提供异步事件驱动的网络应用程序框架,支持高效的网络通信和数据处理。Netty 在性能、可扩展性、安全…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...
智能职业发展系统:AI驱动的职业规划平台技术解析
智能职业发展系统:AI驱动的职业规划平台技术解析 引言:数字时代的职业革命 在当今瞬息万变的就业市场中,传统的职业规划方法已无法满足个人和企业的需求。据统计,全球每年有超过2亿人面临职业转型困境,而企业也因此遭…...
