前言
redis相信每一个java伙伴都知道,但是我相信有一部分大佬已经对redis的使用以及底层实现已经烂熟于心,但是有些java伙伴可能还是停留在使用阶段,至于redis那么多数据类型,底层是怎么样的数据结构,以及实现方式还不是很了解,恰好,笔者最近正在研究这方便,希望精通的大佬,看看我阐述的有没有错误,不明白的小伙伴一起坐下来一起讨论,欢迎大家评论区留言。
因为redis是用c语言写的,笔者的c是个二把刀,所以很多地方如有错误,欢迎指正。
版本
本文采用的redis6.2的版本,如果没有特殊声明,则一直使用该版本。
redis类型的根源
问:什么是redis类型的根源?
答:临近中秋节,大家都要吃月饼了,制作月饼的时候,是需要将原料放到月饼的模具里面;redis也是一样,所有的数据类型都应该有一个东西去存储它,只不过redis的这个模具叫redisObject
redisObject对象
如下代码块来自于server.h中673行~681行
typedef struct redisObject {
//下面语法的含义是:创建一个int类型的,占4位
//unsigned int:4;
//表示类型
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). */
//当前对象的引用次数,0时即释放
int refcount;
//存储数据的位置
void *ptr;
} robj;
type属性
type字段主要表示的redis的类型(确切的说是redis中每个k-v对象的v类型,因为key的类型永远都是String),例如String、List等等。
如下代码块来自于server.h的506行~511行
/*-----------------------------------------------------------------------------
* Data types
*----------------------------------------------------------------------------*/
/* A redis object, that is a type able to hold a string / list / set */
/* The actual Redis Object */
#define OBJ_STRING 0 /* String object. */
#define OBJ_LIST 1 /* List object. */
#define OBJ_SET 2 /* Set object. */
#define OBJ_ZSET 3 /* Sorted set object. */
#define OBJ_HASH 4 /* Hash object. */
encoding属性
encoding字段代表了redisObject底层使用什么数据结构实现的。
例如:set底层使用的intset和hashtable(intset和hashtable底层是两种数据结构,笔者会在后续的日志中分享出来)。
如下代码块来自于server.h的651行~664行
/* Objects encoding. Some kind of objects like Strings and Hashes can be
* internally represented in multiple ways. The 'encoding' field of the object
* is set to one of this fields for this object. */
/**
* 对象编码。某些类型的对象,如string和hash,可以在内部以多种方式表示。
* 对象的'encoding'字段被设置为该对象的其中一个字段。
*/
#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 /* No longer used: old list encoding. */
#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 */
#define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */
例:set的encoding
为了方便大家理解,笔者特意找了set中添加元素的源码来给大家确认一下。
这里主要看的是set的编码,所以具体添加的代码,笔者省略了,后期笔者也会更新。
如下代码块来自于t_set.h的48行~86行
int setTypeAdd(robj *subject, sds value) {
long long llval;
//判断robj类型的形参*subject的encoding属性是否等于OBJ_ENCODING_HT
if (subject->encoding == OBJ_ENCODING_HT) {
//略
//判断robj类型的形参*subject的encoding属性是否等于OBJ_ENCODING_HT
} else if (subject->encoding == OBJ_ENCODING_INTSET) {
//略
} else {
serverPanic("Unknown set encoding");
}
return 0;
}
结论:通过上面的源码,可以非常明确的知道encoding的确存储的是编码类型
lru属性
lru是淘汰策略,redis启动时会读取配置文件中的redis的内存大小,如果redis的内存满了,就是根据不同的策略来进行释放。
refcount属性
refcount存储当前对象的引用次数,用于实际对象的共享,存储对象时,refcount就会增加1,删除对象的时候,refcount就会减少1,如果是0的时候,就会删除。
*ptr属性
*ptr属性存储的是实际存储数据的位置,仍然使用上面提到的set类型为例,*ptr实际指向的就是set底层数据数显对应的intset结构或者是hashtable结构。
总结
看到这里,相信大家对redis有了更加深入的了解,笔者也会在后续的日子中持续更新redis的源码分析,也希望大家耐心等待,一起成长,如果有问题或者有建议,欢迎大家评论区见。。
参考资料:
《Redis5设计与源码分析》 作者陈雷
《Redis设计与实现》 作者黄健宏