redis的几个数据结构——RedisObject

RedisObject是redis类型的核心,每个键值和参数都表示为RedisObject:

typedef struct redisObject {
    // 类型
    unsigned type:4;

    // 编码
    unsigned encoding:4;
    // 对象最后一次被访问的时间,缓存淘汰使用
    //使用LRU算法时存储的是对象访问时间,使用LFU算法时存储的是上次访问时间与访问次数
    //LRU:unsigned的低24 bits的lru记录了redisObj的LRU time。
    //LFU:
//    16 bits      8 bits
//    +----------------+--------+
//    + Last decr time | LOG_C  |
//    +----------------+--------+
//高16 bits用来记录最近一次计数器降低的时间ldt,单位是分钟,低8 bits记录计数器数值counter。
    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;

redis使用RedisObject表示redis中所有键值和参数的原因是

  1. 直接根据类型判断命令是否可行,不需要在其他地方保存键值类型;
  2. 针对不同场景可以采用不同底层结构。

这样看来,分配一个字符串的最小空间19字节,主要包括:

  • RedisObject 16字节
    • 4bits+4bits+24bits+4bytes+8bytes
  • SDS 3字节
    • 字符串比较小时选sdshdr8
    • 1bytes+ 1bytes+ 1bytes

其中,当字符串比较短和比较长时,分别采用的是embstr和raw编码,两者的区别如下图:
redisxiancheng7

如果字符串对象保存的是一个字符串值, 并且这个字符串值的长度小于等于 44 字节, 那么字符串对象将使用 embstr 编码的方式来保存这个字符串值。embstr只调用一次malloc来创建一块内存,把redisObject和sds都放在一起。而raw则需要调用两次malloc,分别创建两个内存块。

例子:小于64字节的字符串的存储格式为embstr,即实际字符串长度44:64-16(RedisObject)-3(SDS)-1(sds中字符串以'\0'结尾)

127.0.0.1:6379> set test defrgthyjukilopoiuytrewqasdfghjkmnbvcxzswedf
OK
127.0.0.1:6379> debug object test
Value at:0x7fd438f33b00 refcount:1 encoding:embstr serializedlength:45 lru:2242706 lru_seconds_idle:5
127.0.0.1:6379> set test defrgthyjukilopoiuytrewqasdfghjkmnbvcxzswedfe
OK
127.0.0.1:6379> debug object test
Value at:0x7fd438e77c20 refcount:1 encoding:raw serializedlength:46 lru:2242723 lru_seconds_idle:1
comments powered by Disqus