redis源码阅读—对象object_redis comparestringobjects

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
* LFU data (least significant 8 bits frequency
* and most significant 16 bits access time). */
int refcount;
void *ptr;
} robj;

其中,ptr指向对象中实际存放的值,这里不需要过多解释,针对其他四个结构体参数,作如下说明:

类型type
Redis的对象有五种类型,分别是string、hash、list、set和zset,type属性就是用来标识这五种数据类型,type占用4个bit位,其取值和类型对应如下:

#define OBJ_STRING 0
#define OBJ_LIST 1
#define OBJ_SET 2
#define OBJ_ZSET 3
#define OBJ_HASH 4

编码类型encoding
Redis对象的编码方式由encoding参数指定,也就是表示ptr指向的数据以何种数据结构作为底层实现,该字段也占用4个bit位,其取值和对应类型如下:

#define OBJ_ENCODING_RAW 0 /* Raw representation */
#define OBJ_ENCODING_INT 1 /* Encoded as integer */
#define OBJ_ENCODING_HT 2 /* Encoded as hash table */
#define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */
#define OBJ_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define OBJ_ENCODING_INTSET 6 /* Encoded as intset */
#define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
#define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */

在Redis3.2.5版本中,zipmap已不再使用,此处也不再讨论。

上述编码类型对应的底层数据结构实现如下表所示:

编码类型底层实现
OBJ_ENCODING_RAW简单动态字符串sds
OBJ_ENCODING_INTlong类型的整数
OBJ_ENCODING_HT字典dict
OBJ_ENCODING_LINKEDLIST双端队列sdlist
OBJ_ENCODING_ZIPLIST压缩列表ziplist
OBJ_ENCODING_INTSET整数集合intset
OBJ_ENCODING_SKIPLIST跳跃表skiplist和字典dict
OBJ_ENCODING_EMBSTREMBSTR编码的简单动态字符串sds
OBJ_ENCODING_QUICKLIST由双端链表和压缩列表构成的快速列表

Redis的每一种对象类型可以对应不同的编码方式,这就极大地提升了Redis的灵活性和效率。Redis可以根据不同的使用场景,来选择合适的编码方式,五种对象类型对应的底层编码方式如下表所示:

对象类型编码方式
OBJ_STRINGOBJ_ENCODING_RAW ,OBJ_ENCODING_INT ,OBJ_ENCODING_EMBSTR
OBJ_LISTOBJ_ENCODING_LINKEDLIST ,OBJ_ENCODING_ZIPLIST ,OBJ_ENCODING_QUICKLIST
OBJ_HASHOBJ_ENCODING_ZIPLIST ,OBJ_ENCODING_HT
OBJ_SETOBJ_ENCODING_INTSET ,OBJ_ENCODING_HT
OBJ_ZSETOBJ_ENCODING_ZIPLIST ,OBJ_ENCODING_SKIPLIST

访问时间lru
lru表示该对象最后一次被访问的时间,其占用24个bit位,保存该值的目的是为了计算该对象的空转时长,便于后续根据空转时长来决定是否释放该键,回收内存

引用计数refcount
C语言不具备自动内存回收机制,所以Redis对每一个对象设定了引用计数refcount字段,程序通过该字段的信息,在适当的时候自动释放内存进行内存回收。此功能与C++的智能指针相似。

  • 当创建一个对象时,其引用计数初始化为1;
  • 当这个对象被一个新程序使用时,其引用计数加1;
  • 当这个对象不再被一个程序使用时,其引用计数减1;
  • 当引用计数为0时,释放该对象,回收内存。

对象基本操作

对象创建

redis提供以下函数用于创建不同类型的对象。

robj *createObject(int type, void *ptr); // 创建对象,设定其参数
robj *createStringObject(const char *ptr, size_t len); // 创建字符串对象
robj *createRawStringObject(const char *ptr, size_t len); // 创建简单动态字符串编码的字符串对象
robj *createEmbeddedStringObject(const char *ptr, size_t len); // 创建EMBSTR编码的字符串对象
robj *createStringObjectFromLongLong(long long value); // 根据传入的longlong整型值,创建一个字符串对象
robj *createStringObjectFromLongDouble(long double value, int humanfriendly); // 根据传入的long double类型值,创建一个字符串对象
robj *createQuicklistObject(void); // 创建快速链表编码的列表对象
robj *createZiplistObject(void); // 创建压缩列表编码的列表对象
robj *createSetObject(void); // 创建集合对象
robj *createIntsetObject(void); // 创建整型集合编码的集合对象
robj *createHashObject(void); // 创建hash对象
robj *createZsetObject(void); // 创建zset对象
robj *createZsetZiplistObject(void); //创建压缩列表编码的zset对象

以创建字符串对象为例,来说明整个redisobject的创建过程。

/*********************************创建字符串对象************************************/
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
robj *createStringObject(const char *ptr, size_t len) {
if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
// 短字符采用特殊的EMBSTR编码
return createEmbeddedStringObject(ptr,len);
else
// 长字符采用RAW编码
return createRawStringObject(ptr,len);
}
/******************************创建RAW编码的字符串对象********************************/
// RAW编码需要调用两次内存分配函数
// 一是为redisObject分内内存,二是为sds字符串分配内存
robj *createRawStringObject(const char *ptr, size_t len) {
// sdsnewlen函数用于创建一个长度为len的sds字符串
return createObject(OBJ_STRING,sdsnewlen(ptr,len));
}
// 通用创建redis对象的函数,采用raw编码方式
robj *createObject(int type, void *ptr) {
robj *o = zmalloc(sizeof(*o));
o->type = type;
o->encoding = OBJ_ENCODING_RAW;
o->ptr = ptr;
o->refcount = 1;

/* Set the LRU to the current lruclock (minutes resolution). */
o->lru = LRU_CLOCK();
return o;
}
/***************************创建EMBSTR编码的字符串对象********************************/
// EMRSTR编码只需要调用一次内存分配函数
// 它的redisobject和sds是放在一段连续的内存空间上
robj *createEmbeddedStringObject(const char *ptr, size_t len) {
robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);
// sds的起始地址sh
struct sdshdr8 *sh = (void*)(o+1);
// 设定redisObject的参数
o->type = OBJ_STRING;
o->encoding = OBJ_ENCODING_EMBSTR;
o->ptr = sh+1;
o->refcount = 1;
o->lru = LRU_CLOCK();
// 设定sds字符串的参数
sh->len = len;
sh->alloc = len;
sh->flags = SDS_TYPE_8;
if (ptr) {
memcpy(sh->buf,ptr,len);
sh->buf[len] = ‘\0’;
} else {
memset(sh->buf,0,len+1);
}
return o;
}

对象释放

Redis不提供释放整个redis对象的函数。每一个redis对象都有一个引用计数,在引用计数变为0的时候对其整体进行释放,下面五个函数分别用来释放对象中存放的数据,其释放过程中需要判断数据的编码类型,根据不同的编码类型调用不同的底层函数。

void freeStringObject(robj *o); // 释放字符串对象
void freeListObject(robj *o); // 释放链表对象
void freeSetObject(robj *o); // 释放集合对象
void freeZsetObject(robj *o); // 释放有序集合对象
void freeHashObject(robj *o); // 释放哈希对象

我们还是以字符串对象为例,来看看对象的释放过程。

// 释放字符串对象
// 无论是embstr编码还是raw编码,其内存上存放的都是sds字符串
// 所以只用调用sdsfree就可以对其进行释放
void freeStringObject(robj *o) {
if (o->encoding == OBJ_ENCODING_RAW) {
sdsfree(o->ptr);
}
}

字符串对象的释放可能看不出来需要根据编码方式来选择不同的底层释放函数,下面来看看集合的释放函数。

void freeSetObject(robj *o) {
switch (o->encoding) {
case OBJ_ENCODING_HT: // 如果编码方式为哈希
dictRelease((dict*) o->ptr);
break;
case OBJ_ENCODING_INTSET: // 如果编码方式为整数集合
zfree(o->ptr);
break;
default:
serverPanic(“Unknown set encoding type”);
}
}

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
e");
}
}

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-S9Y3vNJ2-1713358691982)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值