第一部分的6节简单介绍了Redis的基础数据结构,接下来要开始学习Redis的对象系统。下面(2-1)是redis中对象的实现:
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共有5中:
对象 | 属性 | type 指令输出 |
字符串对象 | REDIS_STRING | string |
列表对象 | REIDS_LIST | list |
哈希对象 | REDIS_HASH | hash |
集合对象 | REDIS_SET | set |
有序集合对象 | REDIS_ZSET | zset |
encoding共8中,指明了*ptr的具体的数据结构,*ptr则是存储具体数据的指针。
Redis中字符串对象针对不同的数据可能有三种编码方式。
整数型的字符串会使用这种编码进行存储,其取值范围为 -9223372036854775808~9223372036854775807(long)
127.0.0.1:6379> SET val1 1
OK
127.0.0.1:6379> type val1
string
127.0.0.1:6379> object encoding val1
"int"
对于长度小于等于39的字符串(3.0之后长度小于等于44),采用embstr编码方式存储。
127.0.0.1:6379> SET val1 abcd
OK
127.0.0.1:6379> SET val2 abcd
OK
127.0.0.1:6379> type val2
string
127.0.0.1:6379> object encoding val2
"embstr"
为什么是39和44这样的数呢?
对于字符串的存储,Redis使用robj+sdshdr实现;每次创建字符串对象程序必须进行两次内存申请,为了提升程序的性能,对于短字符串会一次申请64byte的内存大小(Redis使用jemalloc内存分配器,可以分配8,16,32,64字节等大小的内存),通过1-1、2-1我们可以算出robj需要的内存大小为4x4=16,sdshdr需要的内存大小为4x2=8,那buf的可用大小就是64-16-8=40,再出去'\0'占用的一个空间,则还剩39。
而44则是由于3.2之后redis对于sdshdr实现的修改,因为对于短字符串来说,int型的len和free有点过于浪费了,所以redis分别实现了sdshdr5,sdshdr8,sdshdr16,sdshdr32,sdshdr64。具体的长度大家可以自己自行计算,我在下面贴上这种类型的实现。
typedef unsigned char uint8_t;
struct sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
值得注意的是,Redis并没有为embstr编写任何的修改程序,所以embstr对象实际上是只读的,当我们执行修改命令是,embstr对象会被转为raw对象,修改完成之后不会转换回去。
127.0.0.1:6379> set val4 hello
OK
127.0.0.1:6379> object encoding val4
"embstr"
127.0.0.1:6379> append val4 ' world'
(integer) 11
127.0.0.1:6379> object encoding val4
"raw"
127.0.0.1:6379> set val4 'hello world'
OK
127.0.0.1:6379> object encoding val4
"embstr"
127.0.0.1:6379> SET val3 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678
OK
127.0.0.1:6379> object encoding val3
"embstr"
127.0.0.1:6379> type val3
string
127.0.0.1:6379> SET val3 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789
OK
127.0.0.1:6379> object encoding val3
"raw"
127.0.0.1:6379> type val3
string
出了上述两种情况外,字符串都会是raw编码。