Redis数据结构ZipList,QuickList,SkipList

目录

1.ZipList

1.2.解析Entry:

1.3Encoding编码

1.4.ZipList连锁更新问题

2.QuickList

SkipList跳表

RedisObject

五种数据类型


1.ZipList

redis中的ZipList是一种紧凑的内存储存结构,主要可以节省内存空间储存小规模数据。是一种特殊的双端链表,有一系列连续的内存组成,可以在任意一端进行压入/弹出操作,而且操作的时间复杂度为O(1)

常见方法:

LPUSH mylist "value1" "value2"    //左边插入
RPUSH mylist "value3" "value4"    //右边插入

LPOP mylist    //从链表最左端弹出数据
RPOP mylist    //从链表最右端弹出数据

LRANGE mylist 0 2    //获取左边指定范围的数据
LINDEX mylist 0    //获取指定下标数据

LLEN mylist    //获取列表长度
zlbyteszltailzllenentry

...

entryentryzlend
属性类型长度用途
zlbytesuint32_t4字节记录整个压缩链表占用的内存字节数
zltailuint32_t4字节这个是偏移量,记录压缩列表尾节点到列表起始地址有多少字节,通过这个偏移量,可以确定尾节点地址
zllenuint16_t2字节记录列表包含的节点数量。最大65534,如果超出也只是65535
entry列表节点不定各个节点的长度由节点保存的内容确定
zlenduint8_t1字节特殊值0xFF(255),用于标记压缩例表的末端
1.2.解析Entry:

ZipList的Entry不像普通链表那样记录前后节点的指针,因为记录前后节点指针需要16字节,大大浪费了内存。所以采用下面的结构来保存。

previous_entry_lengthencoding

content

1.previous_entry_length:前一节点的长度,占1字节 或 5字节

当前一节点长度 < 254字节,采用 1字节保存这个长度值

当前一节点的长度 > 254字节,采用5字节保存这个长度值

2.encoding:编码属性,记录content的数据类型(数字/字符串)及长度,占用1,2,5字节。

3.contents:负责保存具体的内容,可以是数字可以是字符串

注意:ZipList中所有储存长度的数值均采用小端字节序储存,低位在前,高位字节在后。

列如:0x1234,采用小端字节序后就是 0x3412

1.3Encoding编码

encoding编码分为两种:字符串和数字

字符串:encoding以00,01,10开头标识储存的是字符串。

下面长度不同的字符串对应encoding编码格式

举例:储存字符串ab,bc

1.4.ZipList连锁更新问题

ZipList的每个Entry都包含previous_entry_length来记录上一节点大小,长度为1个或5字节。

>>如果前一节点长度 < 254,那么采用1字节保存这个长度值。

>>如果前一节点 >= 254,则采用5字节保存这个长度值。

如果有N个连续的长度为250~253之间的entry,如果不巧有一个扩展那么又可能发生连锁反应,又可能所有都发生连锁更新,新增,删除都可能导致连锁更新发生

总结:

1.压缩列表可以看作连续内存空间的“双向链表“

2.列表的节点之间不是通过指针链接,而是上一节点和本节点长度来寻址,大大节省内存

3.如果数据过多,链表过长,可能影响查询性能

4.增加删除都可能发生连续更新问题


2.QuickList

ZIpList虽然节省内存,但是申请内存必须是连续空间,如果内存占用过多那么申请效率变低

数据量过大超出上限采用分片储存数据,那么这些ZipList如何联系?

Redis3.2版本引入QuickList,是一个双端链表,只不过每个节点都是ZipList

Redis提供配置:list-max-ziplist-size 

如果值是正:代表ZipList允许的最大entry数。

如果值是负:代表每个ZipList的最大内存大小。

-1-2-3-4-5
4kb8kb16kb32kb64kb

QuickList可以控制首尾是否进行压缩,通过配置项list-compress-depth来控制,这个参数是控制首尾不压缩的节点个数。

012
代表首尾节点不压缩首尾各有一个1个不压缩,中间全压缩首尾各有2节点不压缩,中间全压缩

QuickList 和 Quick List Node的结构源码:

QuickList结构图:

QuickList特性:

1.每个节点都是ZipList的双端链表

2.节点采用ZipList,解决传统链表的内存占用

3.控制ZipList大小,解决传统连续内存空间申请问题

4.中间节点可压缩,节省内存


SkipList跳表

是个链表,但不是普通链表,不同节点之间跨度不一样。

1.元素按照升序排列储存

2.节点可能包含多个指针,指针跨度不同

为了更快的找到所需要的元素,SkipList采用这种结构类似于二分查找。

以下是结构源码:

// t_zset.c
typedef struct zskiplist {
	// 头尾节点指针
	struct zskiplistNode* header, * tail;
	// 节点数量
	unsigned long length;
	// 最大的索引层级,默认是1
	int level;
} zskiplist;

// t_zset.c
typedef struct zskiplistNode {
	sds ele; // 节点存储的值
	double score;// 节点分数,排序、查找用
	struct zskiplistNode* backward; // 前一个节点指针
	struct zskiplistLevel {
		struct zskiplistNode* forward; // 下一个节点指针
		unsigned long span; // 索引跨度
	} level[]; // 多级索引数组
} zskiplistNode;

特性:

1.跳表是一个双向链表,每个节点包含存储的值和排序用的socre,用来保存别的节点的ele数组

2.节点按照score值排序,score值一样按照ele字典排序

3.每层指针到下一节点跨度不同,层级越高跨度越大

4.增删改查的效率与红黑树基本一致


RedisObject

在Redis中任意数据类型的键和值都会被封装在一个RedisObject中,也叫做Redis对象。

Redis会根据储存不同的数据类型选择不同的编码格式,共包含11种不同类型。

编号编码方式说明
0OBJ_ENCODING_RAW动态字符串(动态长度,非预分配)
1OBJ_ENCODING_INT长整型(用 long 类型存储)
2OBJ_ENCODING_HT哈希表(字典,基于 dict 实现)
3OBJ_ENCODING_ZIPMAP已废弃的哈希压缩结构
4OBJ_ENCODING_LINKEDLIST双端链表(旧版 List 实现)
5OBJ_ENCODING_ZIPLIST压缩列表(紧凑的二进制结构)
6OBJ_ENCODING_INTSET整数集合(仅存储整数的有序集合)
7OBJ_ENCODING_SKIPLIST跳表(有序集合的底层实现之一)
8OBJ_ENCODING_EMBSTR短字符串(固定长度,预分配内存)
9OBJ_ENCODING_QUICKLIST快速列表(双向链表 + 压缩列表)
10OBJ_ENCODING_STREAMStream 流(消息队列结构)

五种数据类型

String基于简单动态字符串SDS实现,存储上限为512mb,基本编码方式是RAM

List可以从首尾操作列表中的元素,3.2版本后统一采用QuickList来实现List

Set:

1.为了查询效率和唯一性,采用Dict编码,key为存储元素,value统一为null

2.所有数据都是整数,且元素数量不超过set-max-intset-entries,Set会采用IntSet编码

ZSet:

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

可以根据score排序,且member必须唯一,可以根据member查询分数

当元素数数量小时,采用ZipList来节省内存,但是需要满足两个条件:

1.元素数量小于 zset_max_ziplist_entries (128)

2.每个元素都小于zset_max_ziplist_value(64)

当数据量大时采用SkipList和HT结构

Hash:

hash底层的编码和ZSet基本一致,只需要把排序有关的SkipList去掉就行。

1.数据量小时,采用ZipList编码节省内存,ZipList中相邻的两个entry保存field和value

2.数据量大时,采用HT编码,就是Dict,触发条件是:元素数量超过512,每个entry超过64字节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值