数据结构
- 字符串
struct sdshdr
{
int len;
int free;
char buf[];
};
注意点:
len若不是0,字符串结尾含有一个'\0',占据1字节空间,但不计入len。
优势:
类似基于字符的动态数组,可动态内存增长与收缩。记录了长度,容量等。保留'\0'来兼容c字符串。
- 链表
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 *ptr, void *key);
}list;
- 基于链式哈希表的字典
typedef struct dictht
{
dictEntry **table;
unsigned long size;
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;
};
typedef struct dict
{
dictType *type;
void *privdata;
dictht ht[2];
int trehashidx;
} dict;
typedef struct dictType
{
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 *key);
void (*valDestructor)(void *privdata, void *obj);
} dictType;
hash = dict->type->hashFunction(key);
index = hash & dict->ht[x].sizemask;
1.为字典的ht[1]分配空间。
如动态扩展,ht[1]的大小等于第一个大于等于ht[0].used*2的2^n
如动态收缩,ht[1]的大小为第一个大于等于ht[0].used的2^n
2.将保存在ht[0]的所有键值对rehash到ht[1]上面。
键-值对在ht[1]上执行插入
3.释放ht[0],将ht[1]变更为ht[0],ht[1]重新设置为空。
注意点:
哈希表存储元素数量规模较大时,一次完整rehash时间会不可接受。
故引入渐进式rehash
1.为ht[1]分配空间,
2.在字典中维持一个索引计数器变量rehashidx,将其设为0
3.rehash期间,每次执行操作时,额外将ht[0]在rehashidx槽上所有键值对rehash到ht[1],此后rehashidx加1
4.rehashidx达到ht[0]槽数时,将其设为-1,表示rehash操作完成。
rehash执行期间,查找先针对ht[0]进行,不成功再对ht[1]进行。添加元素必定落在ht[1]。
- 跳跃表
跳跃表支持平均O(log(N)),最坏O(N)的查找。
实现上相比红黑树简单,可作为红黑树的一种简单替代。
typedef struct zskiplistNode
{
struct zskiplistNode *backward;
double score;
robj *obj;
struct zskiplistLevel
{
struct zskiplistNode *forward;
unsigned int span;
} level[];
}zskiplistNode;
typedef struct zskiplist
{
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
};
- 整数集合
typedef struct intset
{
uint32_t encoding;
uint32_t length;
int8_t contents[];
} intset;
注意:
向一个底层为int16_t数组的整数集合添加一个int64_t类型整数值时,
整数集合已有的所有元素都会被转换成int64_t类型.
升级:发生在新元素类型比现有元素类型长.
1.根据新元素类型,扩展空间大小,新元素空间也需考虑.
2.已有元素变为新类型再放入.
3.插入新元素.
- 压缩列表
typedef struct zlist
{
uint32_t zlbytes;
uint32_t zltail;
uint16_t zlen;
entry nodes[];
uint8_t zlend;
}zlist;
typedef struct zlnode
{
union
{
uint8_t len;
struct
{
char flag;
uint32_t len;
}b;
}previous_entry_length;
union
{
uint8_t a;
uint16_t b;
struct
{
uint8_t d1;
uint32_t d2;
}d;
}encoding;
union
{
}content;
}zlnode;
注意:
结点记录前一结点长度,1字节或5字节性质可能在执行新结点插入,结点删除时导致连锁更新.
对象
Redis基于之前数据结构创建了一个对象系统.
包含字符串对象,列表对象,哈希对象,集合对象,有序集合对象这五种类型的对象.
Redis可在执行命令前,根据对象的类型来判断一个对象是否可执行给定的命令.
可针对不同使用场景,为对象设置多种实现.对象支持引用计数及共享.对象带有访问时间信息.
- 对象的类型与编码
Redis中的每个对象都由一个redisObject结构表示
typedef struct redisObject
{
unsigned type:4;
unsigned encoding:4;
void *ptr;
};
TYPE msg
REDIS_STRING可能通过INT/EMBSTR/RAW编码实现
REDIS_LIST可能通过ZIPLIST/LINKEDLIST编码实现
REDIS_HASH可能通过ZIPLIST/HT编码实现
REDIS_SET可能通过INTSET/HT编码实现
REDIS_ZSET可能通过ZIPLIST/SKIPLIST编码实现
- 字符串对象
字符串对象的编码可以是int, raw或embstr
如一个字符串对象保存的是整数,且可用long表示.
则ptr直接存储值[void*转为long]
如字符串对象保存一个字符串,字符串长度大于39字节,
则将用一个简单动态字符串保存这个字符串值.
如字符串对象保存一个字符串,字符串长度小于等于39字节,
则将用embstr保存.
embstr下redisObject和sdshdr在物理内存和逻辑内存上是连续的
在有需要时,程序会将存在字符串对象里的字符串转为数值,执行数值运算,再将结果以字符串存储.
可用long表示的整数,编码用int
超出long的整数,编码用embstr或raw
- 编码转换
一个int编码的值对象,执行APPEND时,编码切换为raw
对embstr编码的字符串执行修改时,编码切换为raw
- 字符串命令的实现
- 编码转换
列表对象可同时满足以下两条件时,用ziplist编码
a.列表对象保存的所有字符串元素长都小于64字节
b.列表对象保存的元素数量小于512个
不能满足条件a,b的需使用linkedlist
- 列表命令的实现
哈希对象
哈希对象的编码可为ziplist或hashtable
- 编码转换
哈希对象同时满足以下两个条件时,哈希对象用ziplist编码.
1.哈希对象保存的所有键值对的键和值的字符串长度均小于64字节
2.哈希对象保存的键值对数量小于512个.
集合对象
集合对象的编码可是intset或hashtable
intset编码的集合对象用整数集合作为底层实现.
hashtable编码的集合对象用字典作底层实现.
- 编码转换
集合对象满足以下两个条件时,用intset编码
1.集合对象保存的所有元素都是整数值
2.集合对象保存的元素数量不超过512个.
有序集合
有序集合的编码可是ziplist或skiplist
typedef struct zset
{
zskiplist *zsl;
dict *dict;
} zset;
- 编码转换
有序集合对象可同时满足以下两条件时,用ziplist编码
1.有序集合保存的元素数量小于128个
2.有序集合保存的所有元素成员长都小于64字节.
类型检查与命令多态
Redis用于操作键的命令可分为两种类型
体现在,同一操作对象类型不同时,执行不同.
体现在,同一对象,编码类型不同时,执行不同.
内存回收
typedef struct redisObject
{
...
int refcount;
...
}robj;
对象的空转时长
typedef struct redisObject
{
...
unsigned lru:22;
...
}robj;