REDIS底层数据结构
一.redis的字符串
redis底层使用SDS(simple dynamic string) 作为默认字符串表示.SDS还被用作缓冲区,AOF模块的缓冲区以及客户端状态中的输入缓冲区.
1.1 SDS的定义
struct sdshdr{
int len;//字符串长度
int free;//数组中未使用的长度
char buff[];//字符数组
}
与C语言的字符串相比,SDS获取字符串长度的时间复杂度为o(1),SDS还杜绝了缓冲区溢出的情况。
1.2 SDS的空间分配优化策略
1.2.1 空间预分配
如果对SDS进行修改,长度小于1MB,那么程序分配和len属性同样大小的空间.如果大于1M,那么会分配1MB的未使用空间.
1.2.2 惰性空间释放
1.3 二进制安全
C语言默认以空字符结尾,所以不能保存图片,音频等,SDS通过len属性来判断是否到结尾了,所以可以保存二进制数据.
二.redis的链表结构
2.1 链表的数据结构
typedef struct listNode{
listNode *head;
listNode *tail;
unsigned long len;//节点数量
void * dup(void * ptr)//复制函数
void * free(void * ptr)//释放函数
void * match(void * ptr,void * key)//对比函数
}
typedef struct listNode{
struct listNode *prev;
struct listNode * next;
void * value;//节点的值
}
列表被广泛用于实现redis的各种功能,比如列表键,发布于订阅,慢查询,监视器.
三.redis的字典结构(SET)
字典使用哈希表作为底层的实现.
3.1 哈希表的数据结构
typede struct dictht{
dicEntry **table;//哈希表数组
unsigned long size;//哈希表大小
unsigned long sizemark;//掩码用于计算索引值
unsigned long used;//已有节点的数量
}
table是一个数组,每个元素都指向一个dictEntry结构的指针,每个dicEntry保存一个键值对
3.1.1 哈希表节点
typedef struct dictEntry{
void *key;
union{
void *val;
uint64_tu64;
int64_ts64;
}
struct dictEntry * next;
}
3.1.2 字典
typede struct dict{
dictType *type;
void * privdata;//私有数据
dicttht ht[2];//哈希表
//rehash索引,当rehash不在进行时,值为-1
int rehashidx;
}
一般情况下,只用ht[0]哈希表,ht[1]只会在rehash时使用.rehashidx 记录目前rehash的进度.因为dictEntry节点组成的链表没有指向链表表尾的指针,所以总是把新节点添加到链表的表头位置.
当哈希表的键值对过多或者过少,需要对哈希表进行rehash.
为字典ht[1]分配空间
- 如果是扩展操作,ht[1]的大小为第一个大于等于ht[0].used*2的2的n次方,
- 如果是收缩操作,ht[1]的大小为第一个大于等于ht[0].used的2的n次方.
- 将所有ht[0]的键值对rehash到ht[1]上
- 当ht[0]的所有键值对都rehash完毕,释放ht[0]的空间,将ht[1]设置成ht[0],并在ht[1]创建一个空的哈希表.
-
3.1.3 rehash的条件
当以下其中一个条件满足时,会进行rehash
- 服务器目前没有在进行bgsave或者bgrewriteaof,并且负载因子大于等于1
- 服务器目前在进行bgsave或者bgrewriteaof,并且负载因子大于等于5
load_factor = ht[0].used/ht[0].size(负载因子 = 已保存的节点数量/哈希表大小).另一方面, 当负载因子小于0.1的时候,会采取收缩操作.
3.1.4 渐进式rehash
rehash并不是一次性完成的,而是分多次渐进式完成的.这样做的原因在于,如果数据了过大,可能导致服务器在一定时间内停止服务.
以下是rehash的详细步骤:
- 为ht[1]分配空间,让字典同时拥有两个哈希表
- 把rehashidx值设置为0,表示正在进行rehash
- 在rehash期间,对字典的操作都会映射到两个哈希表,同时还会顺带奖ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],rehash完成时,rehashidx自动加一.
- 随着字典操作的不断执行,最终在某个时间点ht[0]的所有键值对会被全部rehash到ht[1]上,这时,rehashidx被设置成-1,表示rehash完成.
-
四.redis的跳跃表(skiplist)
跳表是一种有序的数据结构,采用了用空间换时间的思想来加快访问的速度.它主要用于有序集合键以及集群节点中用做内部结构.
4.1跳表的实现
最左边的结构是zskiplist:
header:指向跳表表头
tail:指向跳表表尾
. level:代表跳表的层数.
length:代表跳表的节点数量
右边的代表zskiplistnode结构:
层:节点中L1代表第一层,L2代表第二次,以此类推
分值(score):各个节点的值,按从小到大排序
成员对象(obj):o1,o2,o3是节点保存的成员变量
3.2跳表的数据结构
typedef struct zskiplistNode {
robj *obj; //节点数据
double score;
struct zskiplistNode*backward; //后退指针
struct zskiplistLevel {
struct zskiplistNode*forward;//前进指针
unsigned int span;//该层跨越的节点数量
} level[];
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode*header, *tail;
unsigned long length;//节点的数目
int level;//目前表的最大层数
} zskiplist;
(PS:每个跳表节点的层高都在0-32之间)
五.整数集合(intset)
整数集合是集合键的底层实现之一,当一个集合只包含整数元素,并且数量不多的时候,会作为集合键的底层实现.
typedef struct intset {
/*
虽然 intset 结构将 contents 属性声明为 int8_t 类型的数组, 但实际上 contents 数组的真正类型取决于 encoding 属性的值:
如果 encoding 属性的值为 INTSET_ENC_INT16 , 那么 contents 就是一个 int16_t 类型的数组, 数组里的每个项都是一个 int16_t类型的整数值
(最小值为 -32,768 ,最大值为 32,767 )。
如果 encoding 属性的值为 INTSET_ENC_INT32 , 那么 contents 就是一个 int32_t 类型的数组, 数组里的每个项都是一个 int32_t类型的整数值
(最小值为 -2,147,483,648 ,最大值为 2,147,483,647 )。
如果 encoding 属性的值为 INTSET_ENC_INT64 , 那么 contents 就是一个 int64_t 类型的数组, 数组里的每个项都是一个 int64_t类型的整数值
(最小值为 -9,223,372,036,854,775,808 ,最大值为9,223,372,036,854,775,807 )。
*/
// 编码方式
uint32_t encoding;
// 集合包含的元素数量
uint32_t length;
// 保存元素的数组
int8_t contents[];
} intset;
如果新插入的元素比现有类型的encoding属性的值要长,就要进行升级.
1.根据新元素的类型,重新计算并分配空间
2.将所有元素都转换成新元素相同的类型,并重新放置.
ps:整数集合不支持降级操作
六.压缩列表(ziplist)
ziplist是列表键和哈希键的底层实现之一.如果数据量少并且都是小整数值或者长度较短的字符串,那么redis就用它作为列表键的底层实现.
6.1 压缩列表的构成
压缩列表是为了节约内存而开发的.一个压缩列表可以包含任意多个节点,每个节点保存一个字节数组或者一个整数值.
属性 |
类型 |
长度 |
用途 |
zlbytes |
uint32_t |
4字节 |
计算整个压缩列表占用的内存字节数 |
zltail |
uint32_t |
4字节 |
记录压缩列表表尾节点距离起始地址有多少字节:上图中p代表起始节点的指针,加上(0x3c=60)就是表尾节点的位置. |
zllen |
uint16_t |
2字节 |
记录节点数量,如果数量大于2^16,就需要遍历整个列表才能知道数量了. |
entry |
|
不定 |
|
zlend |
uint8_t |
1字节 |
(0xFF),用于标记压缩列表的末端 |
6.2压缩列表节点的结构
属性 |
类型 |
长度 |
用途 |
previoud_entry_length |
|
1~5个字节 |
前一个节点的长度 |
encoding |
|
1,2,5字节 |
记录节点content属性保存的类型以及长度 |
content |
|
|
保存节点内容 |
- 大小: 29.1 KB
- 大小: 47.5 KB
- 大小: 68.8 KB
- 大小: 84.2 KB
- 大小: 163.7 KB
- 大小: 277.1 KB
- 大小: 330.7 KB
- 大小: 253.6 KB
- 大小: 281.8 KB
- 大小: 218.5 KB
- 大小: 217.5 KB
- 大小: 226.3 KB
- 大小: 225.9 KB
- 大小: 232.6 KB
- 大小: 204.7 KB
- 大小: 118.2 KB
- 大小: 39.5 KB
- 大小: 49.2 KB
- 大小: 31.9 KB
分享到:
相关推荐
针对Redis的高级数据结构PPT。该PPT一共14页,介绍了Zset的数据结构类型,以及跳表的数据结构。简单阐述了BitMap,HLL,Bloom Filter的原理以及一些常用的指令。针对Bloom Filter有一些自己的见解以及分析
在【大学生 C/C++/JAVA/Python数据结构学习笔记和资料大全】中,你可以找到更多关于数据结构和算法的知识,这些是理解和使用Redis数据结构的基础。通过深入学习和实践,你不仅可以掌握Redis的使用,还能提升自己的...
详细分析redis设计及实现原理 详细分析redis设计及实现原理
总结,Redis流量分析涉及到多个层面,包括数据结构的选择、操作频率的监控、内存管理、持久化策略、网络优化以及使用适当的监控工具。通过对这些方面的深入了解和细致调整,可以有效控制和优化Redis的流量,确保系统...
2. **数据类型丰富**:除了基本的键值对存储,Redis还支持更复杂的数据结构,如列表、集合、有序集合和哈希表,这些数据结构可以满足多种业务需求。 3. **持久化机制**:虽然Redis的主要工作方式是在内存中存储数据...
通过以上分析,我们可以了解到 Redis 的底层数据模型和数据结构是如何设计的,包括 `redisObject` 和 `redisDb` 的细节。这些结构的设计考虑到了内存高效利用、数据访问性能优化以及复杂命令的实现等方面的需求。...
使用场景及目标:本资源适用于想要理解Redis数据结构优化手段及其高级特性的技术人员。有助于更好地进行故障排除和优化Redis应用程序。 使用说明:阅读此内容是为了获得对Redis设计及其实现的理解,从而提高解决实际...
链表是Redis中最基本的数据结构,由adlist.h和adlist.c定义。基本数据结构listNode是最基本的结构,标示链表中的一个结点。结点有向前(next)和向后(prev)两个指针,链表是双向链表。保存的值是void*类型。 链表通过...
通过cpp-RCT,你可以得到关于Redis数据的详细信息,包括键的类型、过期时间、占用的内存大小等,这对于优化Redis的性能和内存管理至关重要。 在cpp-RCT中,有以下几个核心功能: 1. **RDB文件解析**:cpp-RCT能够...
### Redis内存存储结构分析 #### 一、Redis内存存储总体结构概述 Redis是一种高性能的键值存储系统,它将所有数据存储在内存中,从而实现了非常快的数据读写速度。然而,这种设计也有其局限性,例如对于拥有大量...
3. **内存分析**:了解 Redis 数据结构(如 String、Hash、List、Set 和 Sorted Set)的内存占用情况,以及如何通过 Redis 命令获取相关统计信息。 4. **数据收集和排序**:收集每个 Key 的内存占用数据,根据大小...
在面试中,Redis的数据结构是考察候选人技术深度的重要部分。以下将详细介绍Redis中的主要数据结构及其应用。 1. 字符串(String) Redis中的字符串是最基本的数据类型,可以存储任何可打印的字符序列,包括整数和...
墨墨导读:本文节选自《Redis 5设计与源码分析》,主要为读者分析Redis高性能内幕,重点从源码层次讲解了Redis事件模型,网络IO事件重在使用IO复用模型,时间事件重在...系统讲解Redis 5设计、数据结构、底层命令实现,
Redis支持多种数据结构,如字符串(String)、哈希表(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set)。这些数据结构在源码中由不同的C结构体实现,例如`sds`用于表示字符串,`dict`表示哈希表,`list`...
Redis数据结构、原理分析、应用实战 什么是Redis Redis的作用 Redis的存储结构 Redis的安装 Redis的数据类型 字符串类型 列表类型 hash类型 集合类型 有序集合 Redis原理分析 过期时间设置 过期删除的原理 发布订阅 ...
以上是Redis源代码分析中关于基本数据结构的部分总结,通过这些数据结构的支持,Redis能够提供强大的功能和优异的性能。后续章节将进一步探讨Redis的服务器模型、虚拟内存、备份机制等高级特性。
Redis中的跳跃列表(skiplist)是一种高效的数据结构,用于实现有序集合(sorted set)。它是一种概率性数据结构,通过随机概率控制层数,从而在平均情况下提供接近于对数时间复杂度的查找、插入和删除操作。skiplist...