redis6.2.6
一、对象
redis的每个key和value都是一个对象,对象的定义如下。
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表示当前是什么对象
/* 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. */
#define OBJ_MODULE 5 /* Module object. */
#define OBJ_STREAM 6 /* Stream object. */
encoding表示当前对象如何编码
/* 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. */
#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 */
lru表示访问时间(访问次数),用于淘汰策略使用
refcount引用计数
ptr具体的值, 比如key本身的值,value的值
使用了位域,减少不必要的内存开销,整个对象在x86 64bit系统下大小为16字节
二、字符串对象
redis的每个key都是一个字符串对象,而字符串对象又分为普通字符串对象和嵌入式字符串对象。
type: OBJ_STRING,encoding: OBJ_ENCODING_RAW
type: OBJ_STRING,encoding: OBJ_ENCODING_EMBSTR
2.1 原始字符串对象
/* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain
* string object where o->ptr points to a proper sds string. */
robj *createRawStringObject(const char *ptr, size_t len) {
return createObject(OBJ_STRING, sdsnewlen(ptr,len));
}
robj *createObject(int type, void *ptr) {
robj *o = zmalloc(sizeof(*o));
o->type = type;
o->encoding = OBJ_ENCODING_RAW;
o->ptr = ptr;
o->refcount = 1;
/* Set the LRU to the current lruclock (minutes resolution), or
* alternatively the LFU counter. */
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
} else {
o->lru = LRU_CLOCK();
}
return o;
}
可以看出rawStringObject对象和key是分别分配空间的,key是通过sdsnewlen函数分配的空间,当key很小时,频繁的创建这些对象,将容易造成内存碎片,因此引入了嵌入式字符串对象。
2.2 嵌入式字符串对象
当key很小时,将对象和key当作一个整体,分配一个连续的空间,这样就能减少内存碎片的产生。
robj *createEmbeddedStringObject(const char *ptr, size_t len) {
robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);
struct sdshdr8 *sh = (void*)(o+1);
o->type = OBJ_STRING;
o->encoding = OBJ_ENCODING_EMBSTR;
o->ptr = sh+1;
o->refcount = 1;
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
} else {
o->lru = LRU_CLOCK();
}
sh->len = len;
sh->alloc = len;
sh->flags = SDS_TYPE_8;
if (ptr == SDS_NOINIT)
sh->buf[len] = '\0';
else if (ptr) {
memcpy(sh->buf,ptr,len);
sh->buf[len] = '\0';
} else {
memset(sh->buf,0,len+1);
}
return o;
}
2.3 什么时候使用嵌入式字符串对象
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
robj *createStringObject(const char *ptr, size_t len) {
if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
return createEmbeddedStringObject(ptr,len);
else
return createRawStringObject(ptr,len);
}
当字符串长度小于等于44字节时,使用嵌入式字符串对象。
2.4 为啥是44字节
- 首先redisObject大小16字节
- sds最新的结构体大小为3+1(\0字节)=4
这里就已经占用20字节了。而对于内存的分配,实际上不是需要多少就分配多少,一般都是2的次方并且大于等于需要分配的大小的最小值,比如需要5字节,实际分配的8字节(最接近5的2的次方)。redis默认使用jemalloc,其中分配小内存分配4,8,16,32,64。
所以能最大分配的最小空间是64,64-16-4 = 44
, 所以最大的字符串长度为44,小于等于44的都可以在一个块中分配。