Redis五种数据结构详解

String

String是Redis中最常见的数据存储类型:

其基本编码方式是RAW,基于简单动态字符串(SDS)实现,存储上限为512mb。如果存储的SDS长度小于44字节,则会采用EMBSTR编码,此时object head与SDS是一段连续空间。申请内存时只需要调用一次内存分配函数,效率更高。如果string类型的value是数字的话,那么redis会将其转化为Long类型进行存储,进一步节省空间

RAW编码的结构图:

可以看到RAW类型的结构是通过一个指针执行存储的SDS,这样会申请两次内存空间

EMBSTR编码结构图

可以看到 EMBSTR编码时,直接将SDS拼接到RedisObject后面,这样只要申请一次内存空间就行了,而且我们可以发现当长度为44个字节时,SDS部分的长度是1 + 1 + 1 + 44 + 1 = 48,加上前面的RedisObject部分的16,则正好是64字节,正好是2^6,因为内存申请都是申请2的整数次方,所以EMBSTR编码最大限制就是44字节

最后最后就是三种编码结构的对比:

其中前两种编码使⽤的是sds来存储,最后⼀种OBJ_ENCODING_INT编码直接把string存成了long型。 在对string进行incr, decr等操作的时候,如果它内部是OBJ_ENCODING_INT编码,那么可以直接行加减操作;如果它内部是OBJ_ENCODING_RAW或OBJ_ENCODING_EMBSTR编码,那么Redis会先试图把sds存储的字符串转成long型,如果能转成功,再进行加减操作。对⼀个内部表示成long型的string执行append, setbit, getrange这些命令,针对的仍然是string的值(即⼗进制表示的字符串),而不是针对内部表⽰的long型进⾏操作。

List

redis中的list结构可以从首尾插入元素,那么上篇文章提到的哪种数据结构可以满足呢?其实有三种数据结构都可以满足

  • LinkedList :普通链表,可以从双端访问,内存占用较高,内存碎片较多

  • ZipList :压缩列表,可以从双端访问,内存占用低,存储上限低

  • QuickList:LinkedList + ZipList,可以从双端访问,内存占用较低,包含多个ZipList,存储上限高

可以用这样的结构图来表示

值得一提的是插入部分的源码都是调用了一个函数

其中xx参数表示的是key是否存在,如果是true则必须在key存在时才进行插入,默认为false

Set

Set是Redis中的单列集合,满足下列特点:

  • 不保证有序性

  • 保证元素唯一

  • 求交集、并集、差集

所以我们可以使用哈希结构来实现set的这些功能,我们可以看一下源码来验证一下

可以看到,源码中采用了两种结构,还有一个intset,为什么要用intset呢?因为,当存储的是数字的时候,使用intset更加节省空间,但是在存储的时候要做判断,是要要转换成哈希类型

set 的结构图如图所示

可以看到虽然使用的是哈希键值对存储,但是val都设置为null就可以达到只存储值的效果。

这里附上添加元素的源码

可以看到有两种情况需要转换编码格式:

1、intset中的元素超过了上限

2、当前添加的元素不是整数

ZSET

ZSet也就是SortedSet,其中每一个元素都需要指定一个score值和member值:

  • 可以根据score值排序后

  • member必须唯一

  • 可以根据member查询分数

可以满足这些条件的有两个数据结构:哈希表和跳表

哈希表:

可以确保member的唯一性

可以根据member查询分数

跳表:

可以根据score值排序

可以根据member查询分数

可以发现二者都只满足了一部分数据结构,那么把二者结合起来就会可以都满足了

所以看一下对应的源码就可以发现,源码中是维护了两种结构

对应的数据结构如图所示

当元素数量不多时,HT和SkipList的优势不明显,而且更耗内存。因此zset还会采用ZipList结构来节省内存,不过需要同时满足两个条件:

  • 元素数量小于zset_max_ziplist_entries,默认值128

  • 每个元素都小于zset_max_ziplist_value字节,默认值64

ziplist本身没有排序功能,而且没有键值对的概念,因此需要有zset通过编码实现:

  • ZipList是连续内存,因此score和element是紧挨在一起的两个entry, element在前,score在后

  • score越小越接近队首,score越大越接近队尾,按照score值升序排列

有两种情况会将编码方式转化为跳表:

1、zset_max_ziplist_entries 设置为0时,表示禁用了ZipList方式

2、 初始化元素val大于zset_max_ziplist_value时

在添加元素时,也会对元素进行判断是否要转换数据结构和编码方式

使用ZipList存储的时候,采用的相邻存储的方式,即一对表示一个元素,再通过逻辑判断的方式来实现排序,注意这种方式只适用于元素个数较少的情况,当元素个数较少时,性能差别并不大,所以选择采用ZipList进行存储

Hash

刚说完zset,其实hash和zset十分相似,我们可以对比一下二者的异同

Hash结构与Redis中的Zset非常类似:

  • 都是键值存储

  • 都需求根据键获取值

  • 键必须唯一

区别如下:

  • zset的键是member,值是score;hash的键和值都是任意值

  • zset要根据score排序;hash则无需排序

所以redis采取了和zset相似的处理,在数据量较小的时候采用了ZipList,数据量较大的时候采用的是dict结构

当Hash中数据项比较少的情况下,Hash底层才⽤压缩列表ziplist进⾏存储数据,随着数据的增加,底层的ziplist就可能会转成dict,具体配置如下:

hash-max-ziplist-entries 512

hash-max-ziplist-value 64

下面是对应的添加hash元素的源码

其中hashTypeLookupWriteOrCreat主要是判断key是否存在,元素的大小是否超出了hash-max-ziplist-value的限制

循环中hashTypeSet主要是判断添加的数据数量是否超过hash-max-ziplist-entries的限制

对应的结构图如下

若转换成dict则编码方式encoding会改变为OBJ_ENCODING_HT

相关推荐
纪元A梦4 分钟前
贪心算法应用:顶点覆盖问题详解
java·算法·贪心算法
咩咩觉主11 分钟前
c#数据结构 线性表篇 非常用线性集合总结
开发语言·数据结构·unity·c#·游戏引擎·程序框架
地理探险家32 分钟前
各类有关NBA数据统计数据集大合集
数据库·数据集·数据·nba·赛季
bing_15837 分钟前
Spring MVC 中Model, ModelMap, ModelAndView 之间有什么关系和区别?
java·spring·mvc
268572591 小时前
JVM 监控
java·开发语言·jvm
promise5241 小时前
JVM之jcmd命令详解
java·linux·运维·服务器·jvm·bash·jcmd
曼岛_1 小时前
[Java实战]Spring Boot 静态资源配置(十三)
java·开发语言·spring boot
篱笆院的狗1 小时前
MySQL 中如何进行 SQL 调优?
java·sql·mysql
随风奔跑的十八岁1 小时前
java 破解aspose.words 18.6 使用
java·linux·word转pdf·aspose-words
SelectDB技术团队2 小时前
顺丰科技:从 Presto 到 Doris 湖仓构架升级,提速 3 倍,降本 48%
大数据·数据库·数据仓库·信息可视化·数据分析·doris·实时分析