引言
从本次开始,对Redis设计与实现进行阅读及相关读书笔记的记录。Redis版本为3.0
数据结构
简单动态字符串SDS
sds
数据结构位于sds.h/sdshdr
/*
* 保存字符串对象的结构
*/
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
};
相对于C语言的字符串,SDS的优点在于
- 常数复杂度获取字符串长度
- 杜绝缓冲区溢出
- 减少修改字符串所带来的内存重新分配(注意,释放空间时候,不会真的释放,而是设置free的值)
链表
链表的相关代码在adlist.h
中
链表节点listNode
/*
* 双端链表节点
*/
typedef struct listNode {
// 前置节点
struct listNode *prev;
// 后置节点
struct listNode *next;
// 节点的值
void *value;
} listNode;
由多个listNode
组成的双端链表
链表结构list
/*
* 双端链表结构
*/
typedef struct list {
// 表头节点
listNode *head;
// 表尾节点
listNode *tail;
// 节点值复制函数
void *(*dup)(void *ptr);
// 节点值释放函数
void (*free)(void *ptr);
// 节点值对比函数
int (*match)(void *ptr, void *key);
// 链表所包含的节点数量
unsigned long len;
} list;
字典
redis
中的字典使用哈希表实现,其代码在dict.h
中
哈希表结构dictht
/*
* 哈希表
*
* 每个字典都使用两个哈希表,从而实现渐进式 rehash 。
*/
typedef struct dictht {
// 哈希表数组
dictEntry **table;
// 哈希表大小
unsigned long size;
// 哈希表大小掩码,用于计算索引值
// 总是等于 size - 1 比如7号,当计算索引时候, 7&sizemask就可以得到
unsigned long sizemask;
// 该哈希表已有节点的数量
unsigned long used;
} dictht;
其中dictEntry
为一个键值对
/*
* 哈希表节点
*/
typedef struct dictEntry {
// 键
void *key
// 值
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
// 指向下个哈希表节点,形成链表 表明是一个链地址法解决哈希冲突
struct dictEntry *next;
} dictEntry;
下面为了形象表示一个哈希表,给出一个例子
下面给出一个多个dictEntry
连接的哈希表
最终Redis
中的字典数据结构如下
/*
* 字典
*/
typedef struct dict {
// 类型特定函数
dictType *type;
// 私有数据
void *privdata;
// 哈希表
dictht ht[2];
// rehash 索引
// 当 rehash 不在进行时,值为 -1
int rehashidx; /* rehashing not in progress if rehashidx == -1 */
// 目前正在运行的安全迭代器的数量
int iterators; /* number of iterators currently running */
} dict;
/*
* 字典类型特定函数
*/
typedef struct dictType {
// 计算哈希值的函数 redis默认的函数算法为murmurhash2
unsigned int (*hashFunction)(const void *key);
// 复制键的函数
void *(*keyDup)(void *privdata, const void *key);
// 复制值的函数
void *(*valDup)(void *privdata, const void *obj);
// 对比键的函数
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
// 销毁键的函数
void (*keyDestructor)(void *privdata, void *