Redis数据结构与对象

Redis使用五种类型对象实现实现键值对数据库:字符串、列表、哈希、集合、有序集合

  • 字符串编码 int, raw或embstr
    • embstr编码将创建字符串对象所需的内存分配次数从raw编码的两次降低为一次
    • 释放embstr编码的字符串对象只需要调用一次内存释放函数,而raw需要两次
    • embstr编码的字符串对象所有数据都保存在一块连续内存中,比raw编码方式能够更好地利用缓存带来的优势
  • 列表编码 ziplist或linkedlist
  • 哈希编码 ziplist或hashtable
  • 集合编码 intset或hashtable
  • 有序集合编码 ziplist或skiplist

SDS(simple dynamic string)

struct sdshdr{
	int len;
	int free;
	char buf[];
};

与C字符串的区别

  • 常数复杂度获取字符串长度
  • 杜绝缓冲区溢出
  • 减少修改字符串时带来的内存重分配次数
    • 空间预分配
    • 惰性空间释放
  • 二进制安全
  • 兼容部分C字符串函数

双端链表

节点结构

typedef struct listNode {
	struct listNode *prev;
	struct listNode *next;
	void *value; // void* 指针可以用于保存各种不同类型的值
}listNode;

持有结构

typedef struct list {
	listNode *head;
	listNode *tail;
	unsigned long len;
	// 复制链表节点保存的值
	void *(*dup)(void *ptr);
	// 释放链表节点保存的值
	void (*free)(void *ptr);
	// 对比链表节点保存的值和另一个输入是否相等
	void (*match)(void *ptr, void *key);
}list;

特性

  • 双端
  • 无环
  • 带表头指针和表尾指针
  • 带链表长度计数器
  • 多态

哈希表

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

哈希表节点

typedef struct dictEntry {
	void *key;
	union{
		void *val;
		uint64_t u64;
		int64_t s64;
	} v;
	struct dictEntry *next; // 解决冲突
}dictEntry;

字典

typedef struct dict {
	// 类型特定函数
	dictType *type;
	// 私有数据
	void *privdata;
	// 哈希表
	dictht ht[2];
	// rehash 索引
	// 当rehash不在进行时,值为-1
	int trehashidx;
} dict;

type是一个指向dictType结构的指针,每个dictType结构保存了一簇用于操作特定类型键值对的函数,Redis会为用途不同的字典设置不同的类型特定函数

typedef struct dictType {
	// 计算哈希值的函数
	unsigned int (*hashFunction)(const void *key);
	// 复制键的函数
	void *(*keyDup)(void *private, const void *key);
	// 复制值
	void *(*valDup)(void *private, const void *obj);
	// 对比键的函数
	void (*keyCompare)(void *private, const void *key1, const void *key2);
	// 销毁键的函数
	void (*keyDestructor)(void *private, void *key);
	// 销毁值的函数
	void (*valDestructor)(void *private, void *obj);
} dictType;

hash算法

# 使用字典设置的哈希函数,计算键key的哈希值
hash = dict->type->hashFunction(key);

# 使用哈希表的sizemask属性和哈希值,计算出索引值
# 根据情况不同,ht[x] 可以是 ht[0] 或 ht[1]
index = hash & dict->ht[x].sizemask

Redis使用MurmurHash2算法来计算键的哈希值

解决键冲突

Redis使用开链法(separate chaining)来解决地址冲突

渐进式rehash

扩展或收缩哈希表时进行
扩展条件:

  • 没有执行BGSAVE或BGREWRITEAOF命令,哈希表的负载因子大于等于1
  • 正在执行BGSAVE或BGREWRITEAOF命令,哈希表的负载因子大于等于5

收缩条件:
哈希表负载因子小于0.1

步骤:

  1. 为ht[1]分配空间
  2. 将rehashidx设为0
  3. rehash期间,每次对字典执行添加、删除、查找或更新操作时,程序除了执行指定操作外,还会顺带将ht[0]中rehashidx索引上的所有键值对rehash到ht[1],当rehash工作完成之后,程序将rehashidx属性的值增1。(单次rehash)
  4. 随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有键值对都会被rehash至ht[1],这时程序将rehashidx属性值设为-1,rehash完成。

rehash期间,字典的删、改、查会在两个哈希表上进行。新添加到字典的键值对一律保存到ht[1],保证ht[0]包含的键值对数量只会减少不会增加

跳跃表

整数集合

typedef struct intset {
	// 编码方式
	uint32_t encoding;
	// 元素数量
	uint32_t length;
	// 元素
	// 虽为int8_t类型,但实际contents不保存任何int8_t类型的值,
	// contents数组的真正类型取决于encoding属性的值
	int8_t contents[];
} intset;

压缩列表

一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构
组成:

zlbyteszltailzllenentry1entry2entryNzlend

zlbytes: 占用内存字节数。4字节
zltail: 表尾节点(entryN)距压缩列表起点偏移。4字节
zllen: 节点数量。2字节
entryX: 节点。不定
zlend: 标记列表末端,1字节,为0xff

节点构成

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值