redis 核心数据结构

news2024/11/16 3:28:36

一、简述

redis是一个开源的使用C语言编写的一个kv存储系统,是一个速度非常快的非关系远程内存数据库。它支持包括String、List、Set、Zset、hash五种数据结构。

除此之外,通过复制、持久化和客户端分片等特性,用户可以很方便地将redis扩展成一个能够包含数百GB数据和每秒处理上百万次的请求的系统。目前支持多种语言的api,方便用户使用。

redis同时也内置了事务、LUA脚本、复制等功能,提供两种持久化选项,一种是每隔一段时间将数据导入到磁盘(RDB快照模式),另一种是追加命令到日志中(AOF模式)。如果只是作为高效的内存数据库使用也可以关闭持久化功能。

通过哨兵(sentinel)和自动分区(Cuuster)的方式可以提高redis服务器的高可用性。

与关系型数据库相比,redis的命令请求不需要经过查询分析器或查询优化器进行处理,也避免了更新数据时引起的随机读\写,这些慢操作。它直接读写内存中的数据,并且数据是按照一定的数据结构存储的,所以它的速度非常快。

  1. redis命令手册:http://www.redis.cn/commands.html
  2. redis 命令说明:https://www.redis.net.cn/order/

二、数据类型

声明:这里的数据类型是value的数据类型,key的数据类型(区分大小写)都是字符串;

1.1 数据类型的使用场景分别是什么?

Redis 提供了丰富的数据类型,常见的有五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)。

随着 Redis 版本的更新,后面又支持了四种数据类型: BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)。 Redis 五种数据类型的应用场景:

  1. String 类型的应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。
  2. List 类型的应用场景:消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;2. 不能以消费组形式消费数据)等。
  3. Hash 类型:缓存对象、购物车等。
  4. Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。
  5. Zset 类型:排序场景,比如排行榜、电话和姓名排序等。

Redis 后续版本又支持四种数据类型,它们的应用场景如下:

  1. BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总数等;
  2. HyperLogLog(2.8 版新增):海量数据基数统计的场景,比如百万级网页 UV 计数等;
  3. GEO(3.2 版新增):存储地理位置信息的场景,比如滴滴叫车;
  4. Stream(5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。

1.2 五种常见的 Redis 数据类型是怎么实现?

Redis 数据类型和底层数据结构的对应关图,上边是 Redis 6.0 之前版本,现在看还是有点过时了,下边是现在 Redis 7.0 版本的。
在这里插入图片描述

1.3 String 类型的内部实现

String 类型的底层的数据结构实现主要是 SDS(简单动态字符串)。 SDS 和我们认识的 C 字符串不太一样,之所以没有使用 C 语言的字符串表示,因为 SDS 相比于 C 的原生字符串:

  1. SDS 不仅可以保存文本数据,还可以保存二进制数据。因为 SDS 使用 len 属性的值而不是空字符来判断字符串是否结束,并且 SDS 的所有 API 都会以处理二进制的方式来处理 SDS 存放在 buf[] 数组里的数据。所以 SDS 不光能存放文本数据,而且能保存图片、音频、视频、压缩文件这样的二进制数据。
  2. SDS 获取字符串长度的时间复杂度是 O(1)。因为 C 语言的字符串并不记录自身长度,所以获取长度的复杂度为 O(n);而 SDS 结构里用 len 属性记录了字符串长度,所以复杂度为 O(1)。
  3. Redis 的 SDS API 是安全的,拼接字符串不会造成缓冲区溢出。因为 SDS 在拼接字符串之前会检查 SDS 空间是否满足要求,如果空间不够会自动扩容,所以不会导致缓冲区溢出的问题。

1.4 为什么重新设计 SDS 数据结构?

C语言没有Java里面的String类型,只能是靠自己的char[]来实现,字符串在 C 语言中的存储方式,想要获取 「Redis」的长度,需要从头开始遍历,直到遇到 ‘\0’ 为止。所以,Redis 没有直接使用 C 语言传统的字符串标识,而是自己构建了一种名为简单动态字符串 SDS(simple dynamic string)的抽象类型,并将 SDS 作为 Redis 的默认字符串。

C语言的char[]数组 和SDS字符串的对比
在这里插入图片描述
C 源码中体现形式
在这里插入图片描述

1.5 SDS 底层物理编码方式有哪些?

SDS底层物理编码由 int、embstr、raw 三种方式组成。其中embstr 与 raw 类型底层的数据结构其实都是 SDS (简单动态字符串,Redis 内部定义 sdshdr 一种结构)。只有整数才会使用 int,如果是浮点数, Redis 内部其实先将浮点数转化为字符串值,然后再保存。

不同编码类型的对比
在这里插入图片描述
底层数据结构体现
在这里插入图片描述

1.5 List 类型内部实现

Redis3.0 之前

在Redis3.0之前,list采用的底层数据结构是ziplist压缩列表+linkedList双向链表,然后在高版本的Redis中底层数据结构是quicklist(替换了ziplist+linkedList),而quicklist也用到了ziplist。

  1. 如果列表的元素个数小于 512 个(默认值,可由 list-max-ziplist-entries 配置),列表每个元素的值都小于 64 字节(默认值,可由 list-max-ziplist-value 配置),Redis 会使用压缩列表作为 List 类型的底层数据结构;
  2. 如果列表的元素不满足上面的条件,Redis 会使用双向链表作为 List 类型的底层数据结构;

优缺点分析

在这里插入图片描述

Redis3.0 之后

主要通过quicklist实现,它实际上是 zipList 和 linkedList 的混合体,它将 linkedList按段切分,每一段使用 zipList 来紧凑存储,多个 zipList 之间使用双向指针串接起来。
在这里插入图片描述
quicklist就是「双向链表 + 压缩列表」组合,因为一个 quicklist 就是一个链表,而链表中的每个元素又是一个压缩列表

Redis7 实现

因为ziplist存在连续更新问题,所以在redis7 废除 ziplist 底层结构, 使用新的数据结构 listpack 紧凑链表,彻底解决这个问题。
List 使用 quicklist 来存储,quicklist 存储了双向链表,每个节点都是一个 listpack。

redis7 源码体现
在这里插入图片描述

1.6 已有 ziplist,为什么又出 listpack?

listpack 是 Redis 设计用来取代掉 ziplist 的数据结构,它通过每个节点记录自己的长度且放在节点的尾部,来彻底解决掉了 ziplist 存在的连锁更新的问题。

ziplist 的连锁更新问题

1)ziplist存储结构
在这里插入图片描述在这里插入图片描述
2)复现场景

压缩列表新增某个元素或修改某个元素时,如果空间不不够,压缩列表占用的内存空间就需要重新分配。而当新插入的元素较大时,可能会导致后续元素的 prevlen 占用空间都发生变化,从而引起「连锁更新」问题,导致每个元素的空间都要重新分配,造成访问压缩列表性能的下降。

案例说明:压缩列表每个节点正因为需要保存前一个节点的长度字段,就会有连锁更新的隐患

第一步:现在假设一个压缩列表中有多个连续的、长度在 250~253 之间的节点,如下图:
因为这些节点长度值小于 254 字节,所以 prevlen 属性需要用 1 字节的空间来保存这个长度值,一切OK,O(∩_∩)O哈哈~
在这里插入图片描述

第二步:这时,如果将一个长度大于等于 254 字节的新节点加入到压缩列表的表头节点,即新节点将成为entry1的前置节点,如下图:
在这里插入图片描述
因为entry1节点的prevlen属性只有1个字节大小,无法保存新节点的长度,此时就需要对压缩列表的空间重分配操作并将entry1节点的prevlen 属性从原来的 1 字节大小扩展为 5 字节大小。

第三步:连续更新问题出现
在这里插入图片描述
entry1节点原本的长度在250~253之间,因为刚才的扩展空间,此时entry1节点的长度就大于等于254,因此原本entry2节点保存entry1节点的 prevlen属性也必须从1字节扩展至5字节大小。entry1节点影响entry2节点,entry2节点影响entry3节点…一直持续到结尾。

这种在特殊情况下产生的连续多次空间扩展操作就叫做「连锁更新」

1.7 Hash 类型内部实现

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

  1. 如果哈希类型元素个数小于 512 个(默认值,可由 hash-max-ziplist-entries 配置),所有值小于 64 字节(默认值,可由 hash-max-ziplist-value 配置)的话,Redis 会使用压缩列表作为 Hash 类型的底层数据结构;
  2. 如果哈希类型元素不满足上面条件,Redis 会使用哈希表作为 Hash 类型的底层数据结构。
    在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。

1.8 Set 类型内部实现

Redis用整数集合(intset)或 哈希表 hashtable存储set。

  1. 如果集合中的元素都是整数且元素个数小于 512 (默认值,set-maxintset-entries配置)个,Redis 会使用整数集合(intset)作为 Set 类型的底层数据结构;
  2. 如果集合中的元素不满足上面条件,则 Redis 使用哈希表(数组+链表)作为 Set 类型的底层数据结构,key就是元素的值,value为null

1.9 ZSet 类型内部实现

Zset 类型的底层数据结构是由压缩列表或跳表实现的:

  1. 如果有序集合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,Redis 会使用压缩列表作为 Zset 类型的底层数据结构;
  2. 如果有序集合的元素不满足上面的条件,Redis 会使用跳表作为 Zset 类型的底层数据结构;

在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。

三、总结

本章介绍了redis的十大数据结构和它们使用的底层存储原理,为了达到节省内存和快速访问的目的每种数据结构可能有两种存储和访问结构,在必要的时候会由一种结构转换成另一种结构,但这个转换的过程会消耗系统性能和内存空间的,所以在使用的过程中需要注意这些配置参数,开发中尽量避免达到这些峰值,使得redis能够持续的提供高效的服务。

3.1 类型以及常见场景

  1. String 类型的应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。
  2. List 类型的应用场景:消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;2. 不能消费组形式消费数据)等。
  3. Hash 类型:缓存对象、购物车等。
  4. Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。
  5. Zset 类型:排序场景,比如排行榜、电话和姓名排序等。

Redis 后续版本又支持四种数据类型,它们的应用场景如下:

  1. BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总数等;
  2. HyperLogLog(2.8 版新增):海量数据基数统计的场景,比如百万级网页 UV 计数等;
  3. GEO(3.2 版新增):存储地理位置信息的场景,比如滴滴叫车;
  4. Stream(5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。

3.2 底层数据类型对应底层数据结构

1)String (字符串)

1. int:8个字节的长整型。
2. embstr:小于等于44个字节的字符串。
3. raw:大于44个字节的字符串。

Redis会根据当前值的类型和长度决定使用哪种内部编码实现。

2)Hash(哈希)

ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries 配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64 字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的 结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。

hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使 用hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1)。

3)List(列表)

ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置 (默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时 (默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使 用。

linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用 linkedlist作为列表的内部实现。quicklist ziplist和linkedlist的结合以ziplist为节点的链表(linkedlist)

Redis7 开始废弃ziplist(压缩列表)、使用listpack(紧凑链表) 代替。

listpack(紧凑列表):当列表的元素个数小于list-max-listpack-entries配置 (默认512个),同时列表中每个元素的值都小于list-max-listpack-value配置时 (默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使 用。

4)set (集合)

intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个)时,Redis会用intset来作为集合的内部实现,从而减少内存的使用。

hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现。

5)Sorted Set (有序集合)

ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplist- entries配置(默认128个),同时每个元素的值都小于zset-max-ziplist-value配 置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist 可以有效减少内存的使用。

skiplist(跳跃表):当ziplist条件不满足时,有序集合会使用skiplist作 为内部实现,因为此时ziplist的读写效率会下降。

Redis7 开始废弃ziplist(压缩列表)、使用listpack(紧凑链表) 代替。

listpack(紧凑列表):当列表的元素个数小于list-max-listpack-entries配置 (默认512个),同时列表中每个元素的值都小于list-max-listpack-value配置时 (默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使 用。

3.3 底层数据结构时间复杂度

在这里插入图片描述

3.4 数据类型与物理编码对应表

在这里插入图片描述

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

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

相关文章

Ansys Zemax | 如何建立二向分色分光镜

分光镜(Beam splitter)可被运用在许多不同的场合。一般而言,入射光抵达二向分色分光镜(dichroic beam splitter)时,会根据波长的差异产生穿透或反射的现象。这篇文章将说明如何在OpticStudio的非序列模式(non-sequential mode)中建立二向分色分光镜&…

IT技术总监的成长之路

目录 1.CIO之说 2.新职业机会的出现 3.IT主管的两个发展机会 4.七分管理三分技术的CIO 5.七分技术三分管理的CTO 6.CIO的职业规划要求 6.1. 企业战略管理 6.2.信息资源规划 6.3.生产过程管理 6.4. 项目管理 6.5.物流管理 6.6.网络规划与建设 6.7.信息安全技术 6.…

zabbix网络管理安装教程

安装: apt install zabbix-server-mysql zabbix-frontend-php zabbix-nginx-conf zabbix-sql-scripts zabbix-agent 参考资源: 官网: 下载: 其它: 常用指令: 目标与应用价值: 部署难点&#…

友思特新闻|友思特与IDS深化战略合作伙伴关系

尊敬的客户和合作伙伴, 我们非常高兴地宣布,友思特已经与国际领先的机器视觉解决方案提供商 IDS 深化了我们的合作关系。 作为 IDS 的长期合作伙伴,友思特一直致力于为国内客户提供最先进的机器视觉技术和解决方案。 自从友思特与 IDS 合作…

新的~

我昨天加班解决一个概率性出现的bug,这个概率性的问题先介绍下。 の如果正常开机上电,ADC是可以正常工作的,出现问题的时候会开机概率性出现ADC工作不正常,如果工作不正常,重新配置寄存器也不会正常。 の如果开机后ADC…

Ansys Zemax | 光学系统设计中如何使用玻璃替换方法来优化玻璃

在光学系统中选择最优玻璃材料时,Conrady d-D以及模型玻璃等传统的玻璃选择方法提供的帮助有限。本文介绍了如何使用玻璃替换方法进行直接玻璃优化,以及在考虑玻璃的可用性、成本及耐候性等因素时,如何进一步严格挑选玻璃。 简介 玻璃替换方法…

node绿色版本升级

node绿色版本升级 安装绿色版本: 1.非管理员没有安装软件的权限,因此不能直接使用安装包,因此想到了使用绿色版本 2.之前已安装一个版本,因此环境变量的设置已经完成 直接下载绿色的版本,找到之前的安装位置&#xff0…

FX3U PLC高速计数器(摆杆编码器角度测量梯形图代码)

FX3U的高速计数器接线和基本配置,请查看下面文章链接: 三菱FX3U PLC高速计数器应用(附代码)_三菱高速计数器的使用_RXXW_Dor的博客-CSDN博客本文主要以三菱FX3U系列的高速计数为例来讲解,我们简单的看下三菱的编程手册对高速计数器的描述,工业现场建议大家采用AB双向计数…

Gitlab----Shell类型的gitlab-runer设置以root权限执行

【原文链接】Gitlab----Shell类型的gitlab-runer如何设置以root权限执行 1 编辑修改 /etc/systemd/system/gitlab-runner.service 文件,将 --user 修改为 root 2 重启服务 执行如下命令重启 gitlab-runer 服务 systemctl daemon-reload systemctl restart gitlab…

C++项目中mysql的环境配置

第一步创建好项目,选择X64架构 此次项目采用动态库在项目文件夹加入mysql的库分别为libmysql.dll和include 在包含目录中填入相对路径 添加附加依赖项 现在我们写一个开发环境验证代码,检查一下环境是否配置成功 F7生成此时完美运行 至此环境已经配置完成…

VR+中医骨伤学仿真情景实训教学|英途信息

首先,VR骨伤学虚拟实训教学可以突破地域限制。传统的实训教学需要学生亲身前往实地进行观察学习,但这常常受到时间、地点和费用等方面的限制。而通过虚拟现实技术,学生只需佩戴VR头盔,便可随时随地进行学习和实训,无需…

序列化和反序列化:将数据变得更加通用化

序列化与反序列化简介 序列化和反序列化是计算机领域中常用的概念,用于将对象或数据结构转换为字节序列(序列化)和将字节序列转换回对象或数据结构(反序列化)。 序列化是指将对象或数据结构转换为字节序列的过程。通…

Git使用方法与IDEA集成Git

1.Git介绍 1.1版本控制(理解) 无论是代码编写,还是文档编写,我们都会遇到对文档内容反复修改的情况。 1.2开发中存在的问题(理解) 程序员小明负责的模块就要完成了,就在即将提交发布之前的一瞬间,电脑突然蓝屏,硬盘…

使用RNN联合注意力机制实现机器翻译

https://zhuanlan.zhihu.com/p/28834212 具体来自这一篇文章的指导 一、相关使用的查漏补缺: 1.其中的两种神奇的处理字符的操作: 2.关于nn.GRU()的参数解释和用法: http://t.csdn.cn/30PZL 这篇文章讲得很清楚,需要用来预测…

windows下mysql的高可用方案

PS:理论上linux下也可以使用这种方案 环境准备: 首先准备两台电脑,全部安装MySQL,然后分别查看一下ip地址,我的两个ip分别是: 192.168.25.134(简称134) 192.168.25.135(简称135&a…

亚马逊日本站养号测评需要哪些资源和注意点,如何确保账号的稳定性和纯净环境?

日本亚马Amazon.co.jp逊简称日亚,在日本国内亚马逊是アマゾン,日本亚马逊是美国亚马逊在日本成立的分公司,于2000年开通。 目前亚马逊日本站的情况是,流量大,产品少。有很多美国的卖家之间把亚马逊北美站的热卖产品加…

malloc是如何实现内存分配的?【深入理解】

文章目录 前言一、malloc实现原理概括?二、brk() 函数与mmap()函数三、mmap实现原理普通读写与mmap对比mmap内存映射实现过程mmap 的适用场景 前言 在C和C中,malloc函数是用于动态分配内存的常用函数。本文将深入探究malloc函数的内存分配实现机制&…

Linux 定时任务Crontab详解及常见问题解决

Linux 定时任务Crondtab简单了解 crond 是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务 工具,并且会自动启动crond进程,cron…

Python 变量作用域

视频版教程 Python3零基础7天入门实战视频教程 在程序中定义一个变量时,这个变量是有作用范围的,变量的作用范围被称为它的作用域。根据定义变量的位置,变量分为两种。 局部变量。在函数中定义的变量,包括参数,都被称…

Django:四、Djiango如何连接使用MySQL数据库

一、安装数据库第三方插件 安装下载mysql第三方插件 pip install mysqlclient 二、创建MySQL数据库 ORM可以帮助我们做两件事: 创建、修改、删除数据库中的表(不用写SQL语句),但无法创建数据库操作表中的数据(不用…