Redis6.2.5源码阅读(一)五大基础对象

一 字符串对象(String)

字符串在Redis的内存中是以char数组的形式存在的。具体是一个叫做“SDS”的结构体,根据存储数据的char数组的实际使用长度大小,有多种不同的SDS结构体。

/*
   注释:姚子威
   内容:char数组的实际使用长度大于2的32次方小于2的64次方时的SDS结构体
*/
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; //char数组实际使用的长度
    uint64_t alloc; //分配给存储数据的char数组总长度
    unsigned char flags; //SDS结构体类型的标志位
    char buf[];   //真正存储数据的char数组
};

在Redis中,所有对象都有对象头结构体。

/*
   注释:姚子威
   内容:Redis对象头
*/
typedef struct redisObject {
    unsigned type:4;   //对象类型
    unsigned encoding:4; //对象编码
    unsigned lru:LRU_BITS;  //时间戳 用于LRU算法
    int refcount;   //引用计数
    void *ptr;    //实际指向的类型
} robj;

SDS和RedisObject有两种组合形式,当长度特别短的时候,采用embstr形式存储,当长度超过44字节的时候,采用raw形式存储。在内存关系上,采用embstr形式时,RedisObject和SDS内存空间连续,而raw形式下,则不连续。

二 列表对象(List)

列表在使用上可以理解为双向链表。在列表的内部结构是快速列表(quicklist)结构,快速列表的节点是压缩列表(ziplist),一个个压缩列表以链表的形式组合起来形成快速列表。

/*
   注释:姚子威
   内容:快速列表头
*/
typedef struct quicklist {
    quicklistNode *head;
    quicklistNode *tail;
    unsigned long count;     //所有ziplist的槽的总和
    unsigned long len;       //节点的数量
    int fill : QL_FILL_BITS;     //节点中的ziplist槽的数量的最大值
    unsigned int compress : QL_COMP_BITS; //快速列表的压缩程度
    unsigned int bookmark_count: QL_BM_BITS;    //书签数组大小
    quicklistBookmark bookmarks[];       //书签数组
} quicklist;
/*
   注释:姚子威
   内容:快速列表节点
*/
typedef struct quicklistNode {
    struct quicklistNode *prev;
    struct quicklistNode *next;
    unsigned char *zl;          //指向ziplist或者quicklistLZF
    unsigned int sz;            //节点中的ziplist占用了多少比特
    unsigned int count : 16;     //节点中的ziplist槽的数量
    unsigned int encoding : 2;   //编码形式
    unsigned int container : 2;  //节点中存放数据的形式
    unsigned int recompress : 1; //节点之前是否被压缩过
    unsigned int attempted_compress : 1; //测试时使用
    unsigned int extra : 10; //额外分配
} quicklistNode;

三 哈希对象(Hash)

(1)当哈希对象中的元素比较多时,采用字典这种数据结构存储,字典数据结构在Redis中有两个哈希表,正常情况下只有一个哈希表被使用,但是当发生扩容的时候,另外一个哈希表会被启用,用来保存新加入的键值对和从原来的哈希表中迁移过来的键值对。另外值得一提的是,rehash的过程是渐进的,并不是一次性迁移,而是分配在每次对哈希对象增删改查操作中。
(2)当哈希对象中的元素较少时,采用压缩列表的结构存储。

/*
   注释:姚子威
   内容:哈希表
*/
typedef struct dictht {
    dictEntry **table;    //实际数据
    unsigned long size;   //哈希表大小
    unsigned long sizemask; //掩码 用于计算键值对的索引
    unsigned long used;   //哈希表的实际节点数量
} dictht;
/*
   注释:姚子威
   内容:字典
*/
typedef struct dict {
    dictType *type;  //dictType结构体包含一堆特定类型函数指针 可以实现dictType中函数指针的多态 进而实现字典的多态
    void *privdata;   //传给特定类型函数的参数
    dictht ht[2];   //两个HashTable
    long rehashidx; //rehash进度 大于0说明进行中 为-1时结束
    int16_t pauserehash; //rehash是否暂停 为0说明rehash进行中 大于0说明rehash暂停
} dict;

四 集合对象(Set)

(1)底层结构和哈希表一样,不过里面的键值对的值都为NULL值。
(2)当集合中的元素都是整数且比较少的时候,采用整数集合的数据结构。

/*
   注释:姚子威
   内容:整数集合
*/
typedef struct intset {
    uint32_t encoding; //编码方式
    uint32_t length;  //包含的元素数量
    int8_t contents[];   //实际保存元素的数组
} intset;

五 有序集合对象(Zset)

Zset在Set的基础上为集合中的每个元素添加了一个score,通过score可以对元素进行排序。
Zset结构体由一个字典和跳跃链表组合而成,字典的键就是Set集合中的元素,值就是score,同时用跳跃链表来管理每个元素的逻辑关系。

/*
   注释:姚子威
   内容:有序集合
*/
typedef struct zset {
    dict *dict;        //使用字典存储,方便通过指定元素查找score
    zskiplist *zsl;     //管理跳跃列表节点,方便范围查找
} zset;
/*
   注释:姚子威
   内容:跳跃链表
*/
typedef struct zskiplist {
    struct zskiplistNode *header, *tail;    
    unsigned long length;         //跳跃链表长度 除去头节点
    int level;     //跳跃链表高度 除去头节点
} zskiplist;
/*
   注释:姚子威
   内容:跳跃链表节点
*/
typedef struct zskiplistNode {
   struct zskiplistNode *backward;     //后退指针
   double score;       //分值
   sds ele;            //元素值
   struct zskiplistLevel {
        struct zskiplistNode *forward;    //前进指针
        unsigned int span;                //跨度
    } level[];
} zskiplistNode;

下面用一个具体例子来说明zskiplist的查找过程。

(1)头节点的socre是0,元素是NULL。
(2)在跳跃链表中,从头节点到尾节点,在逻辑上是有序的,即从小到大排序。在上图中,所有的score不相同,这样跳跃链表节点之间只比较score的大小就行,如果score相同,还要比较元素的大小,按字符串大小排序。
(3)跳跃链表的查找过程是这样的,定义一个指针X指向头节点,用目标元素比较指针X的最高层的forward指针指向的下一个节点,如果前者大,就更新X指针指向后者,否则就不更新指针X,而是让指针X指向的node节点降层,继续比较大小。
(4)例如,我们要查找的是sds=“i”,那么在上图中,就可以跳过sds="e"直接找到sds=“i”。
(5)每个节点的层数都是在创建时随机确定的。
(6)当Zset中的元素较少时,使用压缩列表的结构保存元素和score。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值