Redis五种数据类型的底层结构

Redis五种数据类型的底层结构

Redis中有一个核心对象叫做redisObject ,用来表示所有的键值对,用redisObject结构体来表示string、hash、list、set、zset这五种基本数据类型。

string 字符串

redis字符串的存储方式有两种:SDS(简单动态字符串)、直接存储(存储对象为整数时使用)

SDS特点:可动态扩容、二进制安全、快速遍历字符串 、兼容传统的C字符串。

string的编码:int、raw、embstr

  • 直接存储,使用int编码

    • int:存储对象为整数时使用
  • SDS存储,使用raw或embstr编码

    • raw:存储对象为长度大于32位的字符串时使用(创建/释放对象时共有两次内存分配:1、创建/释放redisObject对象,2、创建/释放sdshdr结构。此时redisObject和sdshdr并不在一个连续空间内)

    • embstr:存储对象为长度小于等于32位的字符串时使用(创建/释放对象时只有一次内存分配,redisObject和sdshdr都分配在同一个连续的内存空间里)

raw

SDS类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配,可动态扩容。
传统的C字符串与以 ‘\0’ 字符作为结尾,会忽略 ‘\0’ 结尾以后的所有字符,即无法存储带有 ‘\0’ 的字符串数据(比如图片、视频等二进制文件)。Redis的SDS在传统的C字符串基础上增加了一个字符串头(sdshdr),字符串头中的len成员记载了该字符串的长度,并根据len成员来判断该字符串的结尾位置,这意味着它可以存放任何二进制的数据和文本数据,包括 ‘\0’ ,所以SDS是二进制安全的。因为有len成员的存在,获取字符串长度的时间复杂度为O(1)。

list 列表(队列)

在redis 3.2之前,list底层采用ziplist(压缩列表)与linkedlist(双向链表)存储

  • linkedlist:本质上是一个双向链表,每一个节点都是一个存储对象。因为链表存储空间不连续,链表节点在内存中过于分散,因此会产生过多的空间碎片。linkedlist一般用于存储数据较多较大的列表对象。

  • ziplist:类似于字节数组,ziplist的节点在内存中是连续存储的,但是不同于数组,为了节省内存,ziplist的每个节点所占的内存大小可以不同。每个节点可以是一个字节数组或一个整数。只有在满足以下两个条件时,redis才会用ziplist存储列表对象:
    (1)列表对象保存的所有字符串元素的长度均小于64字节。
    (2)列表对象保存的元素数量小于512个。

在redis 3.2,添加了新的数据结构quicklist(快速列表),统一使用quicklist来存储列表对象。

  • quicklist:快速列表也是一个双向链表,quicklist是由ziplist和linkedlist组合而成的,它的每一个节点都是一个ziplist。quicklist既弥补了ziplist占用连续空间过大的缺点,又避免了像linkedlist那样空间占用过于碎片化。但quicklist并非所有节点都为ziplist,quicklist在存储对象时会对列表中间的一批节点进行压缩操作,列表中间被压缩的节点数据结构为quicklistZF(被压缩过的ziplist),列表两端节点数据结构为ziplist。因为两端节点会被频繁操作,所以quicklist不会压缩两端节点,既优化了空间又保证了性能。

quicklist

set 集合

无序集合,集合成员是唯一的,集合内不能出现重复的数据。

set的编码:intset(整数集合)、hashtable(哈希表)。

  • intset:一个整数集合,所有元素都保存在此。能存储三种类型的数据,分别是int16_t、int32_t、int64_t。intset类似于SDS和数组,内存连续。
    集合对象使用intset编码需要满足以下两个条件:
    (1)所有元素都是整数值。
    (2)元素个数小于等于512个。

  • hashtable:底层由字典实现,用字典的key键保存集合对象,字典的value值为null。

sorted set 有序集合(zset)

sorted set的编码:ziplist、skiplist(跳跃表)。

  • ziplist:sorted set使用ziplist存储对象需要满足以下两个条件:
    (1)所有元素长度小于64字节。
    (2)元素个数小于128个。

  • skiplist:skiplist本质上也是一个链表,但它的每个节点都会随机生成不同的层数,是一种概率型数据结构。和平衡树相比,跳跃表的实现更为简单,在做范围查找操作及插入/删除操作时,平衡树都要比跳跃表复杂(进行范围查找时平衡树需要进行中序遍历,较难实现,而跳跃表只需要在找到小值之后,对第一层链表进行遍历就可以实现。进行插入/删除操作时平衡树的子树有可能需要进行一定的调整,而跳跃表只需要简单地修改一下相邻节点的指针。)。

skiplist

跳跃表节点层数生成代码:

int randomizeLevel(double p, int lmax) {
	//初始层数为1
    int level = 1;
    //随机数函数
    Random random = new Random();
    //如果生成的随机数符合条件,则层数+1,进入下一层循环
    while (random.nextDouble() < p && level < lmax) {
    	//层数越高,概率越低
        level++;
    }
    //如果生成的随机数不符合条件,跳出循环,返回当前层数
    return level;
}

如果生成一层的概率为1/2,则生成两层的概率就为1/4,三层为1/8,层数越高,生成的概率越低。

hash 哈希

hash的编码:ziplist、hashtable(哈希表)。

  • ziplist:在ziplist中,键值对是以紧密相连的方式放入的,先放入key,再放入value,键值对总是向表尾添加。hash对象需要满足以下两个条件才能使用ziplist存储:
    (1)所有键值对的键和值的字符串长度都小于64字节。
    (2)键值对数量小于512个。

  • hashtable:底层是字典,hash对象的每一个键值对都直接存入字典中。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值