1. 介绍
redis中基于双端链表、简单动态字符串(sds)、字典、跳跃表、整数集合、压缩列表、快速列表等等数据结构实现了一个对象系统,并且实现了5种不同的对象,每种对象都使用了至少一种前面的数据结构,优化对象在不同场合下的使用效率。
2. 对象的系统的实现
2.1 对象的结构
对象结构robj功能:
- 为5种不同的对象类型提供同一的表示形式。
- 为不同的对象适用于不同的场景,支持同一种对象类型采用多种的数据结构方式。
- 支持引用计数,实现对象共享机制。
- 记录对象的访问时间,便于删除对象。
对象结构定义在redis 3.2版本的server.h
2.2 字符串对象的底层实现类型
编码—encoding | 对象—ptr |
---|---|
OBJ_ENCODING_RAW | 简单动态字符串实现的字符串对象 |
OBJ_ENCODING_INT | 整数值实现的字符串对象 |
OBJ_ENCODING_EMBSTR | embstr编码的简单动态字符串实现的字符串对象 |
2.3 列表对象的底层实现类型
编码—encoding | 对象—ptr |
---|---|
OBJ_ENCODING_QUICKLIST | 快速列表实现的列表对象 |
OBJ_ENCODING_ZIPLIST | 压缩列表实现的列表对象 |
2.4 集合对象的底层实现类型
编码—encoding | 对象—ptr |
---|---|
OBJ_ENCODING_HT | 字典实现的集合对象 |
OBJ_ENCODING_INTSET | 整数集合实现的集合对象 |
2.5 哈希对象的底层实现类型
编码—encoding | 对象—ptr |
---|---|
OBJ_ENCODING_ZIPLIST | 压缩列表实现的哈希对象 |
OBJ_ENCODING_HT | 字典实现的哈希对象 |
2.6 有序集合对象的底层实现类型
编码—encoding | 对象—ptr |
---|---|
OBJ_ENCODING_SKIPLIST | 跳跃表和字典实现的有序集合对象 |
OBJ_ENCODING_ZIPLIST | 压缩列表实现的有序集合对象 |
3. 对象系统的重要操作
3.1创建一个字符串对象
编码为OBJ_ENCODING_RAW
1234567891011robj *createObject(int type, void *ptr) { //创建一个对象robj *o = zmalloc(sizeof(*o)); //分配空间o->type = type; //设置对象类型o->encoding = OBJ_ENCODING_RAW; //设置编码方式为OBJ_ENCODING_RAWo->ptr = ptr; //设置o->refcount = 1; //引用计数为1/* Set the LRU to the current lruclock (minutes resolution). */o->lru = LRU_CLOCK(); //计算设置当前LRU时间return o;}编码为OBJ_ENCODING_EMBSTR
12345678910111213141516171819202122232425/* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is* an object where the sds string is actually an unmodifiable string* allocated in the same chunk as the object itself. *///创建一个embstr编码的字符串对象robj *createEmbeddedStringObject(const char *ptr, size_t len) {robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1); //分配空间struct sdshdr8 *sh = (void*)(o+1); //o+1刚好就是struct sdshdr8的地址o->type = OBJ_STRING; //类型为字符串对象o->encoding = OBJ_ENCODING_EMBSTR; //设置编码类型OBJ_ENCODING_EMBSTRo->ptr = sh+1; //指向分配的sds对象,分配的len+1的空间首地址o->refcount = 1; //设置引用计数o->lru = LRU_CLOCK(); //计算设置当前LRU时间sh->len = len; //设置字符串长度sh->alloc = len; //设置最大容量sh->flags = SDS_TYPE_8; //设置sds的类型if (ptr) { //如果传了字符串参数memcpy(sh->buf,ptr,len); //将传进来的ptr保存到对象中sh->buf[len] = '\0'; //结束符标志} else {memset(sh->buf,0,len+1); //否则将对象的空间初始化为0}return o;}两种字符串对象编码方式的区别
1234567891011121314151617181920212223/* Create a string object with EMBSTR encoding if it is smaller than* REIDS_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is* used.** The current limit of 39 is chosen so that the biggest string object* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. *///sdshdr8的大小为3个字节,加上1个结束符共4个字节//redisObject的大小为16个字节//redis使用jemalloc内存分配器,且jemalloc会分配8,16,32,64等字节的内存//一个embstr固定的大小为16+3+1 = 20个字节,因此一个最大的embstr字符串为64-20 = 44字节#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44// 创建字符串对象,根据长度使用不同的编码类型// createRawStringObject和createEmbeddedStringObject的区别是:// createRawStringObject是当字符串长度大于44字节时,robj结构和sdshdr结构在内存上是分开的// createEmbeddedStringObject是当字符串长度小于等于44字节时,robj结构和sdshdr结构在内存上是连续的robj *createStringObject(const char *ptr, size_t len) {if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)return createEmbeddedStringObject(ptr,len);elsereturn createRawStringObject(ptr,len);}
3.2 字符串对象编码的优化
|
|
3.3 引用计数管理对象
|
|
3.4 对象的复制,创建的对象非共享
|
|
3.5 对象的解码操作
将保存的整数值解码成字符串对象返回回来。