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 种,分别是简单动态字…...
《量化绿皮书》Chapter 3 Calculus and Linear Algebra 微积分与线性代数(二)
《A Practical Guide To Quantitative Finance Interviews》,被称为量化绿皮书,是经典的量化求职刷题书籍之一,包含以下七章: Chapter 1 General Principles 通用技巧 Chapter 2 Brain Teasers 脑筋急转弯 Chapter 3 Calculus and…...
网络安全溯源 思路 网络安全原理
网络安全背景 网络就是实现不同主机之间的通讯。网络出现之初利用TCP/IP协议簇的相关协议概念,已经满足了互连两台主机之间可以进行通讯的目的,虽然看似简简单单几句话,就描述了网络概念与网络出现的目的,但是为了真正实现两台主机…...
BS架构(笔记整理)
楔子.基本概念 1.在网络架构中: 服务器通常是集中式计算资源,负责处理和存储数据;客户机是请求这些服务的终端设备,可能是个人电脑或移动设备;浏览器则是客户机上用来与服务器交互的工具,负责展示网页内容…...
06排序 + 查找(D2_查找(D2_刷题练习))
目录 1. 二分查找-I 1.1 题目描述 1.2 解题思路 方法:二分法(推荐使用) 2. 二维数组中的查找 2.1 题目描述 2.2 解题思路 方法一:二分查找(推荐使用) 3. 寻找峰值 3.1 题目描述 3.2 解题思路 方…...
客户端渲染和服务端渲染
二者本质的区别:是在哪完成了 HTML 的拼接,服务端渲染是在服务端拼接,客户端渲染是在客户端拼接。 服务端渲染的优缺点 优点 SEO 友好,服务端渲染更有利于爬虫爬取信息。 更快的首屏渲染,因为 HTML 已经在服务端生…...
C++ 设计模式 - 访问者模式
一:概述 访问者模式将作用于对象层次结构的操作封装为一个对象,并使其能够在不修改对象层次结构的情况下定义新的操作。 《设计模式:可复用面向对象软件的基础》一书中的访问者模式因两个原因而具有传奇色彩:一是因为它的复杂性&a…...
海云安开发者智能助手(D10)全面接入DeepSeek,赋能开发者安全高效编码新范式
海云安正式宣布完成与DeepSeek(深度求索)的深度技术融合,旗下核心产品D10开发者智能助手全面接入DeepSeek R1模型。此次合作标志着海云安在"AI驱动开发安全"领域实现重要突破。数据显示,通过DeepSeek R1模型的优化与蒸馏…...
服务器绑定 127.0.0.1 和 0.0.0.0 的区别
前言 IP 地址实际上并不是分配给计算机的,而是分配给网卡的,因此当计算机上存在多块网卡时,每一块网卡都会有自己的 IP 地址。 绑定 127.0.0.1 是绑定到 lookback 这个虚拟的本地回环接口,该接口只处理本机上的数据,…...
ML.NET库学习005:基于机器学习的客户细分实现与解析
文章目录 ML.NET库学习005:基于机器学习的客户细分实现与解析项目主要目的和原理目的原理 项目概述实现的主要功能主要流程步骤使用的主要函数方法关键技术 主要功能和步骤功能详细解读详细步骤解析 数据集及其处理步骤数据集处理步骤关键处理步骤原理1. 数据清洗与…...
分布式id探索
一、为什么要使用分布式id? 随着数据量增加,数据需要进行水平拆分,但表自增id无法满足唯一性; 二、分布式id的特点 1唯一性 2 趋势递增、单调递增(数据库中存放的数据结构数据从小到大有序排列)࿰…...
互联网协议套件中的服务类型(RFC 1349)技术解析与总结
1. 背景与核心目标 RFC 1349 是对 IP 协议头部 服务类型(Type of Service, TOS)字段语义的更新与澄清文档,发布于 1992 年。其主要目标包括: 重新定义 TOS 字段的用途:明确 TOS 字段的语义,解决历史标准中的…...
java-初识List
List: List 是一个接口,属于 java.util 包,用于表示有序的元素集合。List 允许存储重复元素,并且可以通过索引访问元素。它是 Java 集合框架(Java Collections Framework)的一部分 特点: 有序…...
【Linux系统】—— 简易进度条的实现
【Linux系统】—— 简易进度条的实现 1 回车和换行2 缓冲区3 进度条的准备代码4 第一版进度条5 第二版进度条 1 回车和换行 先问大家一个问题:回车换行是什么,或者说回车和换行是同一个概念吗? 可能大家对回车换行有一定的误解࿰…...
一文学会:用DeepSeek R1/V3 + AnythingLLM + Ollama 打造本地化部署的个人/企业知识库,无须担心数据上传云端的泄露问题
文章目录 前言一、AnythingLLM 简介&基础应用1.主要特性2.下载与安装3.配置 LLM 提供商4.AnythingLLM 工作区&对话 二、AnythingLLM 进阶应用:知识增强使用三、AnythingLLM 的 API 访问四、小结1.聊天模式2.本地存储&向量数据库 前言 如果你不知道Olla…...
开源身份和访问管理方案之keycloak(一)快速入门
文章目录 什么是IAM什么是keycloakKeycloak 的功能 核心概念client管理 OpenID Connect 客户端 Client Scoperealm roleAssigning role mappings分配角色映射Using default roles使用默认角色Role scope mappings角色范围映射 UsersGroupssessionsEventsKeycloak Policy创建策略…...
C++STL(六)——list模拟
目录 本次所需实现的三个类一、结点类的模拟实现构造函数 二、迭代器类的模拟实现为什么有迭代器类迭代器类的模板参数说明构造函数运算符的重载- -运算符的重载和!运算符的重载*运算符的重载->运算符的重载引入模板第二个和第三个参数 三、list的模拟实现3.1 默认成员函数构…...
HTML5--网页前端编程(下)
HTML5–网页前端编程(下) 9.常用标签下 (1)表格标签 用来展示数据,显示数据,规整条理,可读性好 基本语法 <table><tr> <td>单元格内的文字</td> <td>单元格内的文字</td>… </tr> <tr> <td>单元格内的文字&l…...
Spring 的 ResponseEntity 包装器使用详解
简介 在 Spring 中,ResponseEntity 是 HTTP 响应的包装器。它允许自定义响应的各个方面: HTTP 状态码 响应主体 HTTP 请求头 使用 ResponseEntity 允许完全控制 HTTP 响应,并且它通常用于 RESTful Web 服务中从控制器方法返回响应。 基…...
Git 分布式版本控制工具使用教程
1.关于Git 1.1 什么是Git Git是一款免费、开源的分布式版本控制工具,由Linux创始人Linus Torvalds于2005年开发。它被设计用来处理从很小到非常大的项目,速度和效率都非常高。Git允许多个开发者几乎同时处理同一个项目而不会互相干扰,并且在…...
linux部署ollama+deepseek+dify
Ollama 下载源码 curl -L https://ollama.com/download/ollama-linux-amd64.tgz -o ollama-linux-amd64.tgz sudo tar -C /usr -xzf ollama-linux-amd64.tgz启动 export OLLAMA_HOST0.0.0.0:11434 ollama serve访问ip:11434看到即成功 Ollama is running 手动安装deepseek…...
torch_bmm验算及代码测试
文章目录 1. torch_bmm2. pytorch源码 1. torch_bmm torch.bmm的作用是基于batch_size的矩阵乘法,torch.bmm的作用是对应batch位置的矩阵相乘,比如, mat1的第1个位置和mat2的第1个位置进行矩阵相乘得到mat3的第1个位置mat1的第2个位置和mat2的第2个位置…...
Vue3 特点
不强制要求组件有根节点 // vue2 <template><div><h1>标题</h1><p>内容</p></div> </template>// vue3 <template><h1>标题</h1><p>内容</p> </template> 注意事项 虽然 Vue 3 不再强制…...
mysql8 C++源码中创建表函数,表字段最大数量限制,表行最大存储限制
在 MySQL 8 的 C 源码中,表的最大字段数量限制体现在 MAX_FIELDS 宏定义中。这个宏定义了表中可以拥有的最大字段数量。 代码中的体现 在 mysql_prepare_create_table 函数中,有以下代码段检查表的字段数量是否超过最大限制: cpp if (alt…...
CTFHub-RCE系列wp
目录标题 引言什么是RCE漏洞 eval执行文件包含文件包含php://input读取源代码远程包含 命令注入无过滤过滤cat过滤空格过滤目录分隔符过滤运算符综合过滤练习 引言 题目共有如下类型 什么是RCE漏洞 RCE漏洞,全称是Remote Code Execution漏洞,翻译成中文…...
【OneAPI】通过网页预渲染让搜索引擎收录网页
API简介 网页预渲染,适用于动态网页以及单页面的SEO,支持网页缓存。 您无须更改代码即可让搜索引擎收录您的网页。只要将需要预渲染的页面转发的本接口即可。 如果您使用Nginx作为网页服务器,推荐使用以下配置: #您的网站locat…...
从大规模恶意攻击 DeepSeek 事件看 AI 创新隐忧:安全可观测体系建设刻不容缓
作者:羿莉(萧羿) 全球出圈的中国大模型 DeepSeek 作为一款革命性的大型语言模型,以其卓越的自然语言处理能力和创新性成本控制引领行业前沿。该模型不仅在性能上媲美 OpenAI-o1,而且在推理模型的成本优化上实现了突破…...
【学习笔记】企业数字化转型顶层设计与企业架构TOGAF9.2-第0章 导论
数据要素资产化迈入关键发展期 围绕发挥数据要素乘数作用,研究实施“数据要素x”行动:从供需两端发力,在智能制造、商贸流通、交通物流、金融服务、医疗健康等若干重点领域,加强场景需求牵引,打通流通障碍、提升供给质量…...
Vue3 Ref全家桶深度解析:掌握响应式编程精髓
Vue3 Ref全家桶深度解析:掌握响应式编程精髓 一、Ref核心概念 1.1 响应式数据容器 const count ref(0) // 相当于创建了一个响应式容器: {value: 0,__v_isRef: true,// 其他响应式系统属性 }1.2 全家桶全景图 #mermaid-svg-VkHPjjlo16rOyItj {font-f…...
如何避免大语言模型中涉及丢番图方程的问题
希尔伯特第十问题是一个著名的数学问题,涉及不定方程(又称为丢番图方程)的可解答性。然而在大模型中,我们希望问题都是确定的可解的,或者说要尽可能的想办法避免不确定的不可解问题。由于丢番图方程问题是不可判定问题(即不存在一个有效的算法能够解决该类问题的所有实例…...
