【每日八股】Redis篇(二):数据结构

Redis 数据类型?

主要有 STRING、LIST、ZSET、SET 和 HASH。

STRING

String 类型底层的数据结构实现主要是 SDS(简单动态字符串),其主要应用场景包括:

  • 缓存对象:可以用 STRING 缓存整个对象的 JSON;
  • 计数:Redis 处理命令是单线程,所以执行命令的过程是原子的。故 String 适合技术场景,比如计算访问次数、点赞、转发、库存数量等;
  • 分布式锁:利用 SETNX 命令;
  • 共享 Session 信息:服务器都会去同一个 Redis 获取相关的 Session 信息,解决了分布式系统下 Session 存储的问题;

LIST

LIST 类型底层采用双向链表和压缩列表实现。

  • 若列表元素个数小于 512 个,其元素值小于 64 bytes,Redis 用压缩列表作为 List 类型的底层数据结构;
  • 否则使用双向链表;

Redis 3.2 之后,List 类型底层只由 quicklist 实现。Redis 7.0 之后,压缩列表数据结构被废弃,由 listpack 实现。

LIST 的应用场景包括:

  • 微信朋友圈点赞:要求按照点赞顺序显示点赞好友的信息,如果点赞取消则移除相应的好友信息;
  • 消息队列:可以使用左进右出的命令组成来完成队列的设计。

HASH

Hash 类型的底层数据结构是由压缩列表或哈希表实现的:

  • 如果哈希类型元素个数小于 512 个,所有值小于 64 字节的话,Redis 会使用压缩列表作为 Hash 类型的底层数据结构;
  • 如果哈希类型元素不满足上面条件,Redis 会使用哈希表作为 Hash 类型的底层数据结构。

在Redis 7.0 中,压缩列表数据结构被废弃,交由 listpack 来实现。HASH 应用场景主要有:

  • 缓存对象:一般采用 String + JSON 存储,对象中某些频繁变化的属性可以考虑抽出来用 Hash 类型存储;
  • 购物车:以用户 id 为 key,商品 id 为 field,商品数量为 value,恰好构成了购物车的3个要素。

SET

Set 类型的底层数据结构是由哈希表或整数集合实现的:

  • 如果集合中的元素都是整数且元素个数小于 512个,Redis 会使用整数集合作为 Set 类型的底层数据结构;
  • 如果集合中的元素不满足上面条件,则 Redis 使用哈希表作为 Set 类型的底层数据结构。

为什么 Redis 中的 SET 是 Key-Value 结构?

原因在于,Redis 是一个键值对数据库,其中的所有数据类型(String、List、Hash、Set、Sorted Set 等)都以 key-value 的形式存储。对于 Set 来说,key 是集合名称,value 是集合的内容。

SET 应用的主要场景:

  • 点赞:key 是文章 id、value 是用户 id;
  • 共同关注:Set 类型支持交集运算,故可用来计算共同好友或共同关注的公众号。key 是用户 id,value 是已关注的公众号的 id;
  • 去重:存储某活动中中奖的用户名 ,Set 类型因为有去重功能,可以保证同一个用户不会中奖两次。

ZSET

ZSET 类型(Sorted Set,有序集合)可以根据元素的权重来排序,可以自己来决定每个元素的权重值。比如说,可以根据元素插入Sorted Set 的时间确定权重值,先插入的元素权重小,后插入的元素权重大。应用场景主要有:

  • 在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,可以优先考虑使用 ZSET;
  • 有序集合比较典型的使用场景就是排行榜。例如学生成绩的排名榜、游戏积分排行榜、视频播放排名、电商系统中商品的销量排名等。

BITMAP

bit 是计算机中最小的单位,使用它进行储存将非常节省空间,特别适合一些数据量大且使用二值统计的场景。可以用于签到统计、判断用户登陆态等操作。

HyperLogLog

HyperLogLog用于统计,统计规则是基于概率完成的,不准确,标准误算率是 0.81%。优点是,在输入元素的数量或者体积非常非常大时,所需的内存空间总是固定的、并且很小。比如百万级网页 UV 计数等;

GEO

主要用于存储地理位置信息,并对存储的信息进行操作。底层是由Zset实现的,使用GeoHash编码方法实现了经纬度到Zset中元素权重分数的转换,这其中的两个关键机制就是「对二维地图做区间划分」和「对区间进行编码」。一组经纬度落在某个区间后,就用区间的编码值来表示,并把编码值作为Zset元素的权重分数。

Stream

Redis专门为消息队列设计的数据类型。相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。

之前方法缺陷:不能持久化,无法可靠的保存消息,并且对于离线重连的客户端不能读取历史消息。

Redis 底层数据结构?

SDS

SDS 不仅可以保存字符串,还可以保存二进制数据。

SDS 可以在 O(1) 内获取字符串长度,因为有 Len 属性。

SDS 不会发生缓冲区溢出,因为 SDS 在拼接字符串之前会检查空间是否满足要求,如果空间不够那么就自动扩容,故不会导致缓冲区溢出的问题。

链表

Redis 封装了名为 listNode 的数据结构,并构成双向链表。双向链表包括节点数量len,以及可以自定义实现的 dup、free、match 函数。

  • listNode 链表结点的结构中只包含 prev 和 next 指针;
  • list 提供了 head 和 tail 指针,因此获取链表首尾元素的时间复杂度为O(1)

缺点:

  • 链表每个节点之间的内存都是不连续的,无法很好利用 CPU 缓存。能很好利用CPU缓存的数据结构是数组,因为数组的内存是连续的。
  • 保存一个链表节点的值都需要一个链表节点结构头的分配内存开销较大

压缩列表

压缩列表是由连续内存块组成的顺序型数据结构,类似于数组。不仅可以利用 CPU 缓存,而且会针对不同长度的数据,进行相应编码,这种方法可以有效地节省内存开销。不能保存过多的元素,否则查询效率就会降低;新增或修改某个元素时,压缩列表占用的内存空间需要重新分配,甚至可能引发连锁更新的问题。

缺点:

  • 空间扩展操作也就是重新分配内存,因此连锁更新一旦发生,就会导致压缩列表占用的内存空间要多次重新分配,直接影响到压缩列表的访问性能。
  • 如果保存的元素数量增加了,或是元素变大了,会导致内存重新分配,会有连锁更新的问题。
  • 压缩列表只会用于保存的节点数量不多的场景,只要节点数量足够小,即使发生连锁更新也能接受。

哈希

哈希表是一种保存键值对(key-value)的数据结构。优点在于能以O(1)的复杂度快速查询数据。Redis 采用了拉链法来解决哈希冲突,在不扩容哈希表的前提下,将具有相同哈希值的数据串起来,形成链接。

跳表

跳表(Skip List) 是一种基于有序链表的数据结构,通过添加多级索引来实现高效的查找、插入和删除操作。跳表的平均时间复杂度为 O(log n),接近平衡树的性能,但实现更简单。

跳表的核心思想

一. 多层链表

  • 跳表由多层链表组成,最底层是完整的有序链表,每一层都是下一层的“索引”;
  • 每一层的元素是随机选择的,高层元素少,低层元素多;

二. 跳跃查找:

  • 查找时从最高层开始,如果当前节点的下一个节点小于目标值,则向右移动;否则向下移动一层。
  • 通过跳跃查找,可以快速缩小查找范围。

三. 随机化

  • 插入新节点时,通过随机算法决定节点的高度(即层数),保证跳表的平衡性。
    在这里插入图片描述

整数集合

整数集合本质上是一块连续内存空间。

整数集合有一个升级规则,就是当将一个新元素加入到整数集合里面,如果新元素的类型(int32_t)比整数集合现有所有元素的类型(int16_t)都要长时,整数集合需要先进行升级,也就是按新元素的类型(int32_t)扩展 contents 数组的空间大小,然后才能将新元素加入到整数集合里,升级的过程中也要维持整数集合的有序性。

quicklist

其实 quicklist 就是双向链表 + 压缩列表组合,quicklist 就是一个链表,而链表中的每个元素又是一个压缩列表。quicklist 解决办法,通过控制每个链表节点中的压缩列表的大小或者元素个数,来规避连锁更新的问题。因为压缩列表元素越少或越小,连锁更新带来的影响就越小,从而提供了更好的访问性能。

listpack

listpack 没有压缩列表中记录前一个节点长度的字段了,listpack 只记录当前节点的长度,当向 listpack 加入一个新元素的时候,不会影响其他节点的长度字段的变化,从而避免了压缩列表的连锁更新问题。

为什么用跳表而不用平衡树?

  • 从内存占用上来说,跳表比平衡树更灵活一些:平衡树每个结点包含 2 个指针,而跳表每个结点平均包含 1/(1-p)
  • 在做范围查找的时候,跳表比平衡树操作要简单:在平衡树上,找到指定范围的小值之后,还需要以中序遍历的顺序继续寻找其它不超过大值的节点。如果不对平衡树进行一定的改造,这里的中序遍历并不容易实现。而在跳表上进行范围查找就非常简单,只需要在找到小值之后,对第 1 层链表进行若干步的遍历就可以实现。
  • 从算法的实现难度上来说,跳表比平衡树要简单很多:平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而跳表的插入和删除只需要修改相邻节点的指针,操作简单又快速。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LLM大语言模型私有化部署-使用Dify的工作流编排打造专属AI诗词数据分析师

背景 前面的文章通过 Ollama 私有化部署了 Qwen2.5 (7B) 模型,然后使用 Docker Compose 一键部署了 Dify 社区版平台。 LLM大语言模型私有化部署-使用Dify与Qwen2.5打造专属知识库:在 Dify 平台上,通过普通编排的方式,创建了基于…

Linux虚拟机快照

快照管理 如果在使用虚拟机系统的时候(比如linux),想回到原先的某一个状态,也就是说担心可能有些误操作造成系统异常,需要回到原先某个正常运行的状态 示例: 状态A和状态B处各保存了快照,运行到状态C时发生异常&…

【异常错误】pycharm debug view变量的时候显示不全,中间会以...显示

异常问题: 这个是在新版的pycharm中出现的,出现的问题,点击view后不全部显示,而是以...折叠显示 在setting中这么设置一下就好了: 解决办法: https://youtrack.jetbrains.com/issue/PY-75568/Large-stri…

快速入门Springboot+vue——MybatisPlus多表查询及分页查询

学习自哔哩哔哩上的“刘老师教编程”,具体学习的网站为:7.MybatisPlus多表查询及分页查询_哔哩哔哩_bilibili,以下是看课后做的笔记,仅供参考。 多表查询 多表查询[Mybatis中的]:实现复杂关系映射,可以使…

vscode 配置 Copilot 提示GHE.com连接失败

步骤一:打开设置并进入 settings.json 点击菜单栏中的 “文件” -> “首选项” -> “设置”。 在搜索设置栏中输入 “Copilot: Advanced”。 点击搜索结果下方的 “在 settings.json 中编辑” 链接,这会打开 settings.json 文件。 步骤二&#…

基于拼接的宏基因组全流程

下面是基于组装的宏基因组数据分析流程 目录 基本流程介绍 megahit组装 什么是N50? 基于拼接结果的基因预测 cdhit去冗余 功能注释 宏基因组的分箱操作 分箱的目的: 分箱的原理: 基本流程介绍 单独对每个样本进行基因集组装,得到genome1,2,3…

基于javaweb的SpringBoot酒店管理系统设计和实现(源码+文档+部署讲解)

技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

Grok 3.0 Beta 版大语言模型评测

2025年2月17日至18日,全球首富埃隆马斯克(Elon Musk)携手其人工智能公司xAI,在美国重磅发布了Grok 3.0 Beta版。这款被誉为“迄今为止世界上最智能的语言模型”的AI,不仅集成了先进的“DeepSearch”搜索功能&#xff0…

【R语言】绘图

一、散点图 散点图也叫X-Y图,它将所有的数据以点的形式展现在坐标系上,用来显示变量之间的相互影响程度。 ggplot2包中用来绘制散点图的函数是geom_point(),但在绘制前需要先用ggplot()函数指定数据集和变量。 下面用mtcars数据集做演示&a…

php session数据存储位置选择

PHP session 数据的存储位置可以通过配置文件或者代码来进行设置。默认情况下,session 数据是存储在服务器的文件系统中的。你可以将 session 数据存储在其他地方,例如数据库、缓存等。 基础概念 PHP session默认情况下将数据存储在服务器端的临时文件中…

保姆级! 本地部署DeepSeek-R1大模型 安装Ollama Api 后,Postman本地调用 deepseek

要在Postman中访问Ollama API并调用DeepSeek模型,你需要遵循以下步骤。首先,确保你有一个有效的Ollama服务器实例运行中,并且DeepSeek模型已经被加载。 可以参考我的这篇博客 保姆级!使用Ollama本地部署DeepSeek-R1大模型 并java…

Windows桌面系统管理5:Windows 10操作系统注册表

Windows桌面系统管理0:总目录-CSDN博客 Windows桌面系统管理1:计算机硬件组成及组装-CSDN博客 Windows桌面系统管理2:VMware Workstation使用和管理-CSDN博客 Windows桌面系统管理3:Windows 10操作系统部署与使用-CSDN博客 Wi…

臻识相机,华夏相机,芊熠车牌识别相机加密解密

臻识,华夏,芊熠这三种车牌识别相机解密我都试过了,可以正常解密成功,其它品牌我暂时没有测试。超级简单,免费的,白嫖无敌! 流程: ①:先导出配置文件,例如我以…

RK Android11 WiFi模组 AIC8800 驱动移植流程

RK Android WiFi模组 AIC8800 驱动移植流程 作者:Witheart更新时间:20250220 概要:本文介绍了基于 AIC8800D40 芯片的 WiFi6 模组 BL-M8800DS2-40 在 RK3568 平台上的驱动移植流程。主要涉及环境搭建、驱动代码分析、设备树修改、驱动编译配…

Unity Shader Graph 2D - Procedural程序化图形循环加载进度效果

前言 在游戏中进度加载的效果是一种常见的效果,可以告诉玩家当前游戏处于一个资源加载的状态,这样玩家就能理解游戏不是卡住了或者是出现Bug了,而是正在进行一些数据的处理准备进入下一个场景。 创建一个LineLoading的Shader Graph文件,对应创建一个材质球,然后在…

蓝桥杯备考:贪心算法之矩阵消除游戏

这道题是牛客上的一道题,它呢和我们之前的排座位游戏非常之相似,但是,排座位问题选择行和列是不会改变元素的值的,这道题呢每每选一行都会把这行或者这列清零,所以我们的策略就是先用二进制把选择所有行的情况全部枚举…

Java网络编程封装

系列文章目录 Java知识点 文章目录 系列文章目录👉前言👉一、封装的目标👉二、套接字层封装👉壁纸分享👉总结 👉前言 Java 网络编程封装原理主要围绕着将底层的网络通信细节隐藏起来,提供简洁…

百度首页上线 DeepSeek 入口,免费使用

大家好,我是小悟。 百度首页正式上线了 DeepSeek 入口,这一重磅消息瞬间在技术圈掀起了惊涛骇浪,各大平台都被刷爆了屏。 百度这次可太给力了,PC 端开放仅 1 小时,就有超千万人涌入体验。这速度,简直比火…

边缘安全加速(Edge Security Acceleration)

边缘安全加速(Edge Security Acceleration,简称ESA)是一种通过将安全功能与网络边缘紧密结合来提升安全性和加速网络流量的技术。ESA的目标是将安全措施部署到接近用户或设备的地方,通常是在网络的边缘,而不是将所有流…

SpringBoot+Mybatis-Plus实现动态数据源

目录 一、前言二、代码实现1)工程结构2)相关依赖3)数据源拦截切面4)动态数据源切换5)核心配置类6)使用 三、原理分析1)mapper接口注入流程2)动态数据源切换执行流程 四、声明式事务导…