Redis是一个key-value型数据库,key只能是字符串,value可以是字符串、列表、集合、有序集合和散列表,这5种数据类型用结构体robj表示,我们称之为Redis对象。
#define LRU_BITS 24
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; //缓存淘汰使用
int refcount; //引用计数
void *ptr;
} robj;
redisObject的type字段表示具体的数据类型,如下
#define OBJ_STRING 0
#define OBJ_LIST 1
#define OBJ_SET 2
#define OBJ_ZSET 3
#define OBJ_HASH 4
redisObject的encoding字段表示具体当前对象底层存储采用的数据结构
type类型 | encoding类型 | 备注 |
---|---|---|
OBJ_STRING | raw,int,embstr | raw与embstr都是sds |
OBJ_LIST | quicklist | |
OBJ_SET | dict,intset | |
OBJ_ZSET | dict,ziplist | |
OBJ_HASH | ziplist,skiplist+dict |
raw与embstr区别
redisObject的ptr字段是void*类型的指针,指向实际存储的某一种数据结构,但是当redisObject存储的数据可以用long类型表示时,数据直接存储在ptr字段。可以看出,为了创建一个字符串对象,必须分配两次内存,redisObject与sds存储空间;两次内存分配效率低下,且数据分离存储降低了计算机高速缓存的效率。因此提出OBJ_ENCODING_EMBSTR(embstr)编码的字符串,当字符串内容比较短时,只分配一次内存,robj与sds连续存储,以此提升内存分配效率与数据访问效率。
redisObject的refcount存储当前对象的引用次数,用于实现对象的共享。共享对象时,refcount加1;删除对象时,refcount减1,当refcount值为0时释放对象空间。删除对象的代码如下:
void decrRefCount(robj *o) {
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;
…………
}
zfree(o); //释放对象空间
} else {
//引用计数减1
if (o-refcount != OBJ_SHARED_REFCOUNT) o-refcount--;
}
}
robj的refcountlru字段占24比特,用于实现缓存淘汰策略,可以在配置文件中使用maxmemory-policy配置已用内存达到最大内存限制时的缓存淘汰策略。lru根据用户配置的缓存淘汰策略存储不同数据,常用的策略就是LRU与LFU。LRU的核心思想是,如果数据最近被访问过,那么将来被访问的几率也更高,此时lru字段存储的是对象访问时间;LFU的核心思想是,如果数据过去被访问多次,那么将来被访问的频率也更高,此时lru字段存储的是上次访问时间与访问次数。