Redis数据结构之redisObject(三)

178 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

RedisObject

因为 Redis 的数据类型有很多,而且,不同数据类型都有些相同的元数据要记录(比如最后一次访问的时间、被引用的次数等),所以,Redis 会用一个RedisObject 结构体来统一记录这些元数据,同时指向实际数据.

typedef struct redisObject {
    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;
  • type: 对象的数据类型。占4个bit。可能的取值有5种:OBJ_STRING, OBJ_LIST, OBJ_SET, OBJ_ZSET, OBJ_HASH,分别对应Redis对外暴露的5种数据结构(即我们在第一篇文章中提到的第一个层面的5种数据结构)。
  • encoding: 对象的内部表示方式(也可以称为编码)。占4个bit。可能的取值有10种,即前面代码中的10个OBJ_ENCODING_XXX常量
  • lru: 做LRU替换算法用,占24个bit
  • refcount: 引用计数。它允许robj对象在某些情况下被共享
  • ptr: 数据指针。指向真正的数据。比如,一个代表string的robj,它的ptr可能指向一个sds结构;一个代表list的robj,它的ptr可能指向一个quicklist。

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 */

对于redis中基础的五种数据类型都会存在不同的编码集.

string
  1. OBJ_ENCODING_RAW: string采用原生的表示方式,即用sds来表示。
  2. OBJ_ENCODING_INT string采用数字的表示方式,实际上是一个long型。
  3. OBJ_ENCODING_EMBSTR string采用一种特殊的嵌入式的sds来表示,后续章节讨论.
list
  1. OBJ_ENCODING_ZIPLIST
  2. OBJ_ENCODING_QUICKLIST
  3. OBJ_ENCODING_LINKEDLIST在新版本已经被淘汰
hash
  1. OBJ_ENCODING_HT
  2. OBJ_ENCODING_ZIPLIST

当redis中存在的如下配置: 当两者都满足时才会使用ziplist,若某一个不满足则采用OBJ_ENCODING_HT存储

hash-max-ziplist-entries 512
hash-max-ziplist-value 64
set
  1. OBJ_ENCODING_INTSET
  2. OBJ_ENCODING_HT.

对于OBJ_ENCODING_INTSET编码集适用于数值类型,且元素个数不超过512的情况下.其他情况直接采用OBJ_ENCODING_HT.

set-max-intset-entries 512
sorted set
  1. OBJ_ENCODING_SKIPLIST
  2. OBJ_ENCODING_ZIPLIST

对于OBJ_ENCODING_ZIPLIST适用于如下场景,元素个数不超过128,value大小不超过64字节.

zset-max-ziplist-entries 128
zset-max-ziplist-value 64
lru

lru应用于内存淘汰策略中lru,lfu算法.具体原理后续进行分析.

  1. LRU time
  2. LFU data

robj的引用计数操作

oid incrRefCount(robj *o) {
    o->refcount++;
}

void decrRefCount(robj *o) {
    if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");
    if (o->refcount == 1) {
        switch(o->type) {
        case OBJ_STRING: freeStringObject(o); break;
        case OBJ_LIST: freeListObject(o); break;
        case OBJ_SET: freeSetObject(o); break;
        case OBJ_ZSET: freeZsetObject(o); break;
        case OBJ_HASH: freeHashObject(o); break;
        default: serverPanic("Unknown object type"); break;
        }
        zfree(o);
    } else {
        o->refcount--;
    }
}

我们特别关注一下将引用计数减1的操作decrRefCount。如果只剩下最后一个引用了(refcount已经是1了),那么在decrRefCount被调用后,整个robj将被释放。

注意:Redis的del命令就依赖decrRefCount操作将value释放掉。

总结

robj表示的就是Redis对外暴露的第一层面的数据结构:string, list, hash, set, sorted set,而每一种数据结构的底层实现所对应的是哪个第二层面的数据结构(dict, sds, ziplist, quicklist, skiplist, 等),则通过不同的encoding来区分。可以说,robj是联结两个层面的数据结构的桥梁.