简介redis之集合类型数据

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简介redis之集合类型数据相关的知识,希望对你有一定的参考价值。

参考技术A

在之前的文章中我们有提及string类型是通过底层的SDS(简单动态字符串)实现的,那么其他呢?
除了String类型外,list,set,sorted set,hash这四种都有两种底层实现结构,通常情况我们称这四种类型为集合类型, 特点是一个键对应了一个集合的数据 :
list由双向链表和压缩列表实现
hash由压缩列表和哈希表实现
sorted set由跳表和压缩列表实现
set 由整数数组和哈希表实现

·1.压缩列表在查找和更新的时间复杂度方面没有很大的优势,为什么redis要把他作为底层数据结构?
2.集合数据的操作效率

list由双向链表和压缩列表实现

阅读src/ziplist.h和src/ziplist.c我们惊奇的发现一件事情:
redis竟没有提供一个结构体保存压缩列表的信息,而是提供了一组宏来定义每个成员的地址

压缩列表是一系列 特殊编码的连续内存块 组成的顺序序列数据结构,可以包含任意多个节点(entry),每一个节点可以保存一个字节数组或者一个整数值。

这个数据结构是不是乍一看很像数组,每一个元素entry保存了一个数据,和数组不同的是表头有三个字段:
1.zlbytes标识整个压缩列表长度
2.zltail标识列表的偏移量,表尾点entryN距离压缩列表起始地址的字节数
3.zllen标识列表中entry个数
列表表尾还有一个zlend标识结束
那么我们就可以快速定位第一个元素(zlbytes-10)和最后一个元素(zlbytes-1)了

ziplist的每个节点由3个部分组成:
1.prevlen表示前一个节点的长度
2.encoding表示节点的编码格式
3.entry-data:压缩后的数据

zpilist格式

<zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>

zlbytes:ziplist占总字节数
zltail:最后一个元素的偏移量,相当于ziplist的尾指针。
zllen:entry元素个数
zlend :ziplist结束标志位
entry:ziplist的各个节点

ziplist的entry 的格式:

<prevlen> <encoding><len> <entry-data>

prevlen :前一个元素的长度,相当于节点保存前一个元素的指针。
encoding: 记录了当前节点保存的数据的类型以及当前节点长度,相当于节点保存后一个元素的指针。
len:自身长度
entry-data :经过压缩后的数据

quciklist结构实现

其中节点实现

我们阅读结构体发现虽然是quicklist但却出现了很多关于ziplist的身影
首先ziplist的插入和删除操作时间复杂度非常高(参考数组需要重新生成一个ziplist来作为更新后的list),当一个list非常大的且更新平凡就会带来非常大的开销。
所谓的quicklist就是把ziplist和普通的双向链表解和,每个双向节点保存一个ziplist,每个ziplist中存一批list数据,这样可以避免大量链表指针操作带来的内存开销(即化整为零思想)

Redis对外暴露的list数据结构,其底层实现所依赖的内部数据结构就是quicklist。quicklist就是一个块状的双向压缩链表。

考虑到双向链表在保存大量数据时需要更多额外内存保存指针并容易产生大量内存碎片,以及ziplist的插入删除的高时间复杂度,两个数据结构的缺陷会导致在数据量很大或插入删除操作频繁的极端情况时,性能极其低下。

Redis为了避免数据结构在极端情况下的低性能,将双向链表和ziplist综合起来,成为了较双向链表及ziplist性能更加稳定的quicklist

1.压缩列表在查找和更新的时间复杂度方面没有很大的优势,为什么redis要把他作为底层数据结构?
list底层使用压缩列表本质上是将所有元素紧挨着存储,能节省空间避免内存碎片(内存空间不是硬盘空间是很昂贵的)

有序列表查找元素,只能遍历查找,于是就出现了跳表

skiplist节点结构体

也就是说跳表在链表的基础上增加了多级索引,通过索引位置的几个跳转,实现数据的快速定位
其中索引可以持续累加

整数数组是集合的底层实现之一,当一个集合只包含整数元素且数量不多,就会用该方式实现

2.集合数据的操作效率

[Redis6]常用数据类型_Set集合

Set集合

简介

Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。

Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。

一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变

数据结构

Set数据结构是dict字典,字典是用哈希表实现的。

Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。

常用命令

sadd [key][value1][value2 …

将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略

smembers [key] 取出该集合的所有值。

sismember [key][value]判断集合[key]是否为含有该[value]值,有1,没有0

scard[key] 返回该集合的元素个数。

srem [key][value1][value2] … 删除集合中的某个元素。

spop [key]随机从该集合中吐出一个值。

srandmember [key][n] 随机从该集合中取出n个值。不会从集合中删除 。

smove [source][destination] value把集合中一个值从一个集合移动到另一个集合

sinter [key1][key2] 返回两个集合的交集元素。

sunion [key1][key2] 返回两个集合的并集元素。

sdiff [key1][key2] 返回两个集合的差集元素(key1中的,不包含key2中的)

以上是关于简介redis之集合类型数据的主要内容,如果未能解决你的问题,请参考以下文章

Redis之Set

[Redis6]常用数据类型_Set集合

Redis五大常见的数据类型之 Hash

集合之集合的简介

redis之持久化操作

集合类简介