Redis底层数据结构

SDS 简单动态字符串

C语言字符串:以空字符’\0’结尾

SDS结构:

struct sdshdr {
	int len;// 记录buf中已使用的字节数量,等于SDS字符串的长度
	int free; // 记录buf中未使用的字节数量
	char buf[]; // 字节数组,遵循用空字符结尾
}

与C字符串的区别
1. 获取字符串长度常数复杂度
2. 杜绝缓存区溢出,如果C字符串使用超过了分配的空间,则溢出
3. 减少字符串修改带来的内存重新分配次数(空间预分配——需要扩展空间时,小于1M,预分配一样大小的空闲,大于1M,预分配1M空闲,惰性释放,不会真的释放,而是放在free中,可以调用指定api释放)
4. 二进制安全,非以’\0’结尾
5. 兼容部分C字符串api

链表

列表键中包含了很多元素,或者列表中的元素都是很长的字符串,redis会使用链表来作为列表键的底层实现。除此之外,发布订阅,慢查询,监视器等也用到了链表。

typedef struct listNode {
	struct listNode *prev;
	struct listNode *next;
	void *value;
} listNode;
typedef struct list {
	listNode *head; // 头
	listNode *tail; // 尾
	unsigned long len; // 链表长度
	void *(*dup)(void *ptr); // 复制函数
	void (*free)(void *ptr); // 释放函数
 	int (*match)(void *prt, void *key);// 对比函数
} list;

字典

redis数据库就是用字典实现的,也是哈希键的底层实现之一,如果一个哈希键包含较多元素或者值都是较长的字符串,就会使用字典实现。

typedef struct dictht {
	// 哈希表数组
	dictEntry **table;
	// 哈希表大小
	unsigned long size;
	// 哈希表大小掩码,用于计算索引值,总是等于size - 1
	unsigned long sizemask;
	// 哈希表已有节点数量
	unsigned long used;
} dictht;


typedef struct dictEntry {
	void *key;
	union {
		void *value;
		uint64_tu64;
		int64_ts64;
	} v;
	struct dictEntry *next;
} dictEntry;

// 字典对象
typedef struct dict {
	// 类型
	dictType *type;
	// 私有数据
	void *privdata;
	// 哈希表
	dictht ht[2];
	// rehash索引
	int trehashidx; /*rehashing not in progress if trehashidx == -1*/
} dict;

type和privdata是为多态字典而设置,type保存了特定字典的计算函数,privdata为可选的私有数据。ht是一个size=2的数组,一般使用ht[0],进行rehash时会使用ht[1],当rehash进行时trehashidex为其进度,没有在rehash时为-1.

  1. 哈希函数,索引计算
    hash = dict -> type -> hashFunction(key)
    index = hash & dict -> ht[0].sizemask
    MurmurHash2作为hash算法。
  2. 冲突解决,使用链地址法。
  3. rehash
    • ht[1] 分配空间
      扩容 ht[1]的大小为大于等于ht[0]的used * 2的 第一个2的n次幂。收缩 ht[1]的大小为大于等于ht[0]的used 的第一个2的n次幂。
    • 将ht[0]上所有的键值保存到ht[1]上,
    • 释放ht[0],然后将ht[1]设置为ht[0]
    • 当没有在进行bgsave或者aoprewrite的时候,负载因子大于等于1或者在进行bgsave或者bgrewriteaof时负载因子大于等于5,自动进行扩容,当负载因子小于0.1时,自动缩容.
    • 负载因子 = ht[0].used / ht[0].size
    • 渐进式rehash,为了避免服务停止,使用trehashidex记录rehash进度。在rehash期间,每次对字典进行操作时,除了正常的操作,还会将trehashidx上的键值复制到ht[1]上,操作完成后trehashidx +1
    • rehash期间,ht[0]和ht[1]同时提供服务,查询会先查询ht[0],然后再查询ht[1],新增操作一律在ht[1]上执行。

跳跃表

跳跃表是一种有序的数据结构,他通过在一个节点中维护多个指向其他节点的指针,来快速访问节点。是有序集合的一种实现。

复杂度:平均O(logn)最坏 O(n)

跳跃表节点

typedef struct zskiplistNode {
	// 层
	struct zskiplistLevel {
		zskiplistNode *forward;// 前进指针
		unsigned int span; // 跨度
	} zskiplistLevel[];

	// 后退指针
	struct zskiplistNode *backward;
	// 分值
	double score;
	// 成员
	robj *obj;
}zskiplistNode;
  1. 每次创建节点时,根据幂次定律随机生成一个1-32的数作为层数组的大小。层的跨度表示两个节点之间的距离
  2. score & obj score可以重复,按照score排序,obj不能重复

整数集合

集合键的实现之一,只包含整数,且元素不多时,使用该数据结构。

升级

压缩列表

实现列表和hash键

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值