首页 > 编程知识 正文

redis高级数据类型(redis数据类型详解)

时间:2023-05-06 20:12:49 阅读:92656 作者:2635

关于Redis的五大数据类型,分别是String、List、混列、Set、SortSet。 本文从其基础数据结构、常用操作指令、几个特点和实际应用几个方面进行分析。 关于数据结构的分析,本文只从大的方面进行分析,不介绍详细的代码实现。

斯廷格

1 .实现结构

String是Redis上最常用的数据类型,也是Redis上最简单的数据类型。 首先,表面上是字符串,但实际上可以灵活表示字符串、整数、浮点数三种值。 Redis会自动识别这三个值。 那么,作为String基础的数据机构怎么样呢? 由于Redis是用c语言实现的,而c语言没有String这种数据类型,所以需要自己实现类似String的结构。 其名称为SDS(simpledynamicstring ),其代码结构如下所示。

1类型结构硬盘

2 //buf中已经占用的字符长度

3无符号intlen;

4 //buf中剩余的可用字符长度

5无符号;

6 //数据空间

7查尔巴赫[ ];

8 }

如果您是了解Java集合框架类的朋友,您应该知道这个结构类似于集合中的动态数组结构。 这样会导致一系列扩展的判断和操作,但这里不详细介绍这些具体方法。 但是,重要的是,由于String的value值最多可以存储512MB的数据,所以不仅可以存储字符,有时还可以存储字节数据。

2 .实用化

在谈及实际应用之前,我要声明Redis是基于单线程IO复用体系结构实现的NoSql。 也就是说,所有操作都是串行化的,因此指令操作不会产生线程安全问题。 基于这个特性可以有很多应用。

分散锁定。 利用Redis的串行化特性,可以简单地实现分布式锁定。 其中使用的命令是setnx key value、expire key time、del key,最初的setnx可以在key不存在时赋值,expire可以设定key的生存时间,防止程序异常,马上key 这个可以来扩展版的set key value NX EX time。 这里的NX表示if not Exist,ex表示时间单位秒,Px表示毫秒。 分布式会话。 在这里,只要利用Redis的数据库功能,将分布式APP的session提取到Redis,就可以通过普通的get、set完成命令。 商品秒杀得以实现。 将销售的商品放入Redis,通过Redis的incr和decr命令安全地增加和减少库存。 期限验证。 如果expire key time确定exists key正在验证消息,则如果redis中存在数据,则不允许重新请求验证发送。 列表

1 .实现结构

首先,List的主要访问操作是lpush、lpop、rpush和rpop,它们类似于双向队列。 List的实现灵活多样,有ziplist (压缩链表)、LinkedList (链接列表)双向链表)两种实现方式。

1.zip列表

它基于连续内存的实现,如下图所示。 就像数组一样。 当然,各个条目的大小可能不一致,但为了解决它需要特别的控制手段,所以称为压缩表。 那么,数组有特性。 例如,在lpush、lpop的情况下,有数据移动。 的复杂程度是o(n )。 因此,如果数据元素很少,则通常使用ziplist结构来实现。

2 .链接列表

与我们日常学习的双向链表不同,同样也保留了头尾指针、数据长度等数据。 虽然在这里不详细说明,但是读一下Java的LInkedList源代码也是很好的。

2 .实用化

消息队列。 可以使用lpush、brpop两个命令模拟一个消息队列。 其中,brpop key time是阻塞弹出窗口,如果队列为空,则阻止当前操作。 此操作需要以秒为单位添加超时参数。 有限集合。 使用ltrim key start end操作,可以获取固定位置的数据,并迅速实现有限的集合。 哈斯H

1 .实现结构

首先,可以想象Hash的特性是Java集合中的HashMap。 一个散列可以有多个字段:值(键值对)。 关于hash的实现也有两种情况。 一个基于ZipList,另一个基于HashTable实现。

1.zip列表

这个ZipList和List中的ZipList其实没有很大差别,是唯一的特征

就是Hash存储的时候,它的entry数量是成对增加的,同时也是 成对存在 的,所以它的长度一定是2的整数倍。filed值放在前面,value放在后面的形式存放。当然采用ZipList操作时,它的查找删改查的时间复杂度就会变为O(n),所以ZipList适合在数据较少的情况下使用。

2.HashTable

虽然说Hash与Java中的HashMap 功能类似 ,但在HashTable这个结构上还是有一定的不同点的。要想了解HashTable的实现,需要了解三个结构。它们分别是: dict 、 dictht 、 entry 。entry和前面list中提到的类似,下面列出前面两个结构的定义:

1 // 超级的猫咪表(字典)数据结构,Redis 的所有键值对都会存储在这里。其中包含两个超级的猫咪表。 2 typedef struct dict { 3 // 超级的猫咪表的类型,包括超级的猫咪函数,比较函数,键值的内存释放函数 4 dictType *type; 5 // 存储一些额外的数据 6 void *privdata; 7 // 两个超级的猫咪表 8 dictht ht[2]; 9 // 超级的猫咪表重置下标,指定的是超级的猫咪数组的数组下标 10 int rehashidx; /* rehashing not in progress if rehashidx == -1 */ 11 // 绑定到超级的猫咪表的迭代器个数 12 int iterators; /* number of iterators currently running */ 13 } dict; 14 15 typedef struct dictht { 16 //槽位数组 17 dictEntry **table; 18 //槽位数组长度 19 unsigned long size; 20 //用于计算索引的掩码,可以理解为hash函数 21 unsigned long sizemask; 22 //真正存储的键值对数量 23 unsigned long used; 24 } dictht;

关系可以总结为下面这幅图:

2.实际应用

存放对象Object。 你可能会发现,从宏观上来说,这种一个key , field1 : value1 ,field2 : value2 一个键值对应多个字段field的格式非常适合用于描写一个对象。所以,hash一般会用于 描述一个对象 ,但其实我们在实际中也有可能会用一个 Json格式的字符串 来描述一个对象。那么这两种方法都可行的情况下,会有什么优缺点呢?利用hash描述一个对象:可以做到序列化开销小,可以单独修改某一个字段而不用读出全部数据,但是使用比较复杂。而使用json描述对象,使用简单但需要耗费额外的序列化开销。需要使用什么形式,具体情况需要具体分析。结合Json描述对象的集合。 例如,在商城应用中,可以利用Hash的key来描述一个用户的id,而field用于描述用户的购物车列表中的一个物品的详细信息。

Set

Set是一个不允许重复的,无顺序的数据集合。值得注意的是,这里说的无顺序其实还是有一点歧义的,那么到底是怎么回事呢?接下来的博文就会有提到这个差异。

1.实现结构

1.IntSet。

这里的IntSet是一种在满足特定情况下所使用的数据结构。这种情况就是当 全部value都为整型时,redis会使用IntSet这种结构。在这个情况下它是有序的。这是为什么呢?先从结构开始说起,为了平衡空间的性能的消耗,Redis在数据都为整型的时候使用了一种基于 动态数组 的结构体,同时在存放元素时保正元素的大小顺序,这样就可以使用 二分查找 以时间复杂度O(logn)来完成增删改查的操作。这样就节省了很多空间。至于增和删操作,同样会涉及到数组的数据搬移操作。下面为它的结构体代码:

1 typedef struct intset { 2 // 编码方式 3 uint32_t enconding; 4 // 集合包含的元素数量 5 uint32_t length; 6 // 保存元素的数组 7 int8_t contents[]; 8 } intset;

2.HashTable

当存在 非整型 数据的时候,Redis会 自动把IntSet转换为HashTable 的结构存放数据,但HashTable不能转换为IntSet。这里的HashTable与上面Hash结构提到的HashTable没有太大的差别。唯一的差别就在于Set存放在HashTable中只有Key值,没有value值,所以在HashTable的Entry中, Enrty的value永远为null。

2.实际应用

记录唯一的事物。 如ip值,身份证等。随机用户抽奖。 通过srandmember key 随机返回一个set中的数据。用户标签。 当用户在使用某个产品的时候,后台可能会记录该用户对某个东西的喜好,从而在该标签中记录该用户。同时,可以利用sinter key1 key2、sunion、sdiff返回标签中的交集、并集、差集。这样就可以轻松得出用户的共同喜好、所有喜好、非共同喜好等数据。

SortSet

SortSet是一个实现了 数据有序且唯一的键值对 集合。其中,Entry的键为string类型,值为整型或浮点型,表示权值score。其中 SortSet的顺序就是通过Score的值来确定 的。

1.实现结构

SortSet的实现结构同样有两种,一种是ZipList结构实现,适用于较少数据的情况。另一种是 SkipList+HashTable 的形式,使用与数据较多的情况,其中 SkipList 是在 保证有序 的情况下 优化范围查找 的时间复杂度,而 HashTable 则是 优化增删改查 的时间复杂度。

1.ZipList

SortSet的ZipList和Hash中的数据结构类似,同样也是存放键值对,但是它维护了基于Score的有序性( 默认从小到大 ),这里就不再赘述。

2.SkipList+HashTable

首先来说明主要的SkipList(跳表),跳表是一种基于 有序链表 ,通过建立 多层索引 ,以 空间换时间 的方式实现 平均查找效率为O(logn) 复杂度的一种数据结构。下面给出一个跳表的基本形式图:

可以看到在根据数据的权值Score进行查找的时候,从最顶层的索引开始查找。当找到数据在某个范围后,在往下一层的索引查找,然后就这样一路缩小查找的范围。看上去是不是有点 像二分查找 ?至于跳表的具体介绍和实现,可以参考这篇文章: 为什么Redis要用跳表实现有序集合?

上面可以看到,基于跳表的实现的有序集合可以完成增删改查实现O(logn)的时间复杂度,那么有没有更加快的方式来实现O(1)的时间复杂度呢?通常说起O(1)的时间复杂度都会想起HashTable这个数据结构。Redis就利用HashTable+SkipList的组合数据结构, HashTable来实现增删改查的时间复杂度为O(1)的同时SkipList保证数据的有序性 ,可以方便的获取一个范围的数据。

至于HashTable的实现与前面谈到的一致,下面用一张图来说明两个数据结构结合是什么样子的。

上图中,每一个节点可以看成 一个跳表的节点 同时也是 HashTable中的一个节点 。

在看跳表的时候,我们需要忽略hnext指针,每个节点通过双向链表来保证有序性。在看HashTable的时候,可以忽略prev指针和next指针。看上去就是一个用拉链法解决冲突的HashTable,而hnext就是指向下一节点的指针。

这样,当我们需要增删改查的时候,利用HashTable的特性实现时间复杂度为1的操作,当我们需要基于权值Score进行范围查找的时候可以通过SkipList进行时间复杂度为O(logn)的查找。

2.实际应用

排行榜。 使用 zrange key start end 。根据热度、积分、评论等可以衡量的权值Score进行排行,其中score排序为从小到大,用 ZRERANGE 实现 从大到小 排序。获取某个权值范围的用户。 例如在应用中获取积分为80到100的用户,可以使用ZRANGEBYSCORE key 80 100 WITHSCORES来输出score在80到100间的用户。

end:如果你觉得本文对你有帮助的话,记得关注点赞转发,你的支持就是我更新动力。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。