暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

面试:Redis五种基本类型的底层数据结构有哪些?

波波的小书房 2021-08-10
3145

1、Redis数据类型

面试的时候关于Redis有哪些数据类型的问题,基本上是最常问的,五种基本数据类型string、list、set、zset、hash也是信手拈来。

不过随着Redis版本的更新迭代,还有四种数据类型不为大家所知,它们分别是bitmap、GEO、HyperLogLog,以及Redis5.0中的Streams。

Redis中的数据类型及底层的数据结构是以编码的方式标识的,下面看看五种基本数据类型的底层数据结构到底有哪些。


1.1、五种基本类型编码表

注意:type命令用于查看键的类型。

redis类型(type命令输出 )
类型编码
string
REDIS_STRING
list
REDIS_LIST
set
REDIS_SET
zsetREDIS_ZSET
hash
REDIS_HASH


1.2、底层数据结构编码表

注意:object encoding命令用于查看键的底层数据结构。

底层数据结构
底层数据结构编码
object encoding命令输出
long类型的整数
REDIS_ENCODING_INTint
embstr编码的简单动态字符串
REDIS_ENCODING_EMBSTRembstr
简单动态字符串REDIS_ENCODING_RAWraw
双向链表
REDIS_ENCODING_LINKEDLISTlinkedlist
压缩链表REDIS_ENCODING_ZIPLISTziplist
整数集合
REDIS_ENCODING_INTSETintset
跳跃表和字典
REDIS_ENCODING_SKIPLISTskiplist
字典
REDIS_ENCODING_HThashtable


1.3、五种基本类型与底层数据结构关联表

redis类型编码
底层数据结构编码
REDIS_STRINGREDIS_ENCODING_INT
REDIS_STRINGREDIS_ENCODING_EMBSTR
REDIS_STRINGREDIS_ENCODING_RAW
REDIS_LISTREDIS_ENCODING_ZIPLIST
REDIS_LISTREDIS_ENCODING_LINKEDLIST
REDIS_SETREDIS_ENCODING_INTSET
REDIS_SETREDIS_ENCODING_HT
REDIS_ZSETREDIS_ENCODING_ZIPLIST
REDIS_ZSETREDIS_ENCODING_SKIPLIST
REDIS_HASHREDIS_ENCODING_ZIPLIST
REDIS_HASHREDIS_ENCODING_HT


2、Redis数据类型的编码转换时机

从1.3中的表我们可以看到,redis的某种数据类型可能对应多种数据结构。通过编码这种机制来将类型与数据结构关联起来,大大提高了Redis的灵活性。一个类型可以有多种数据结构,这样设计的好处归根结底是为了节省空间、提高效率

下面介绍一下Redis五种基本数据类型的编码转换时机,以及简单介绍一下底层数据结构。


2.1、简单动态字符SDS

由于Redis是C语言编写的,那么先来了解一下C语言的字符串。

C语言使用长度为N+1的字符数组来表示长度为N的字符串,并且字符数组的最后一个元素总是空字符'\0'。

C语言这种简单的表示字符串的方式,并不能满足Redis对字符串在安全性、效率以及功能等方面的要求,因此Redis自己构建了SDS抽象数据类型。

Redis在一些对字符串值无须修改的地方才会使用C字符串,比如打印日志。

比起C字符串,SDS有以下优点:

  • 常数复杂度获取字符串长度;

  • 杜绝缓冲区溢出;

  • 减少修改字符串长度时所需的内存重分配次数;

  • 二进制安全;

  • 兼容部分C字符串函数;


2.2、string类型三种编码的转换

string类型有int、embstr和raw三种编码。

如果一个string类型的值是整数,且这个整数在long范围内,则采用int编码;如果一个string类型的值是字符串且小于32字节,则采用embstr编码,大于32字节则采用raw编码。

embstr是专门用于保存短字符串的一种编码。


2.3、list类型两种编码的转换

list类型有ziplist和linkedlist两种编码。

当list中每个元素都小于64字节,且总长度小于512时,使用ziplist,否则使用linkedlist。

压缩列表是为了节约内存。


2.4、set类型两种编码的转换

set类型有intset和hashtable两种编码。

当set中每个元素都是整数,且总长度小于512时,使用intset,否则使用hashtable。

使用hashtable时,hashtable的key就存储的是set的元素,而hashtable的value恒为null。


2.5、zset类型两种编码的转换

zset类型有ziplist和skiplist两种编码。

当zset中每个元素都小于64字节,且总长度小于128时,使用ziplist,否则使用skiplist。

那么ziplist是如何存储有序集合的呢?

首先有序集合除了元素本身之外,每个元素还有一个分值,ziplist是按分值排序的,具体存储方式如下所示。

苹果
5.5
香蕉
6.5
西瓜
8.0

skiplist这种编码其实包含了两种数据结构:跳跃表和字典dict,本文3节简单介绍了跳跃表这种数据结构。

跳跃表按分值从小到大的顺序存储了zset所有元素,而字典dict存储的是元素与分值的映射,这两种数据结构通过指针共享元素和分值,因此不会产生内存重复的浪费。

那为什么要用两种数据结构表示呢?

  • 字典dict能在O(1)时间内根据元素查找分值,这一点跳跃表办不到;

  • 跳跃表是有序的,因此能快速进行范围查找,而字典dict是无序的,因此这一点字典办不到;

因此结合两种数据结构的优点就是提升性能。


2.6、hash类型两种编码的转换

hash类型有ziplist和hashtable两种编码。

当hash中每个key和value都小于64字节,且总长度小于512时,使用ziplist,否则使用hashtable。

用ziplist存储时具体的存储结构如下所示。

name
张三
age
10
sex


3、跳跃表

普通单链表查询一个元素的时间复杂度为O(n),即使该单链表是有序的,我们也不能通过2分的方式缩减时间复杂度。

跳跃表是一种有序数据结构,跳跃表平均时间复杂度O(logn)、最坏时间复杂度O(n),大部分情况下,跳跃表效率可以与平衡二叉树相提并论,并且比平衡二叉树更简单。


一个理想的跳跃表如下图所示,每一层的结点数都是下面一层的二分之一,这跟二分法差不多。



但是如果要在理想的跳跃表上增删元素,调整一个理想的跳跃表将是一个比调整平衡二叉树还复杂的操作,因此在使用编程语言去实现的时候,并不会实现一个理想的跳跃表。

当Redis的有序集合中元素比较多的时候,会采用跳跃表这种数据结构。




文章转载自波波的小书房,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论