本文主要说明Redis key-value结构中封装五种value的
redisObject
结构。
I、上帝视角
redisObject
结构主要说明value对象的底层编码方式,以及实际指向等内容:
/*src/redis.h/redisObject */
typedef struct redisObject {
// 刚刚好32 bits
// 对象的类型,字符串/列表/集合/哈希表
unsigned type:4;
// 未使用的两个位
unsigned notused:2; /* Not used */
// 编码的方式,Redis 为了节省空间,提供多种方式来保存一个数据
// 譬如:“123456789” 会被存储为整数123456789
unsigned encoding:4;
// 当内存紧张,淘汰数据的时候用到
unsigned lru:22; /* lru time (relative to server.lruclock) */
// 引用计数
int refcount;
// 数据指针,指向真正的数据
void *ptr;
} robj;
下面对以上属性进行一一说明。
II、type属性
redisObject
数据结构将对象属性与对象的数据分开,这样做有良好的特性,可以先根据属性进行检查判断等操作,这些都不需要直接访问数据本身。
其中type属相标记了value对象的数据类型:
/* Object types */
#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4
III、encoding属性
Redis为优化内存,对每种type类型都至少会有两种底层实现方式,如zset就可以是ziplist与skiplist。
redisObject
结构中的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 REDIS_ENCODING_RAW 0 /* Raw representation */
#define REDIS_ENCODING_INT 1 /* Encoded as integer */
#define REDIS_ENCODING_HT 2 /* Encoded as hash table */
#define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap,已经淘汰 */
#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define REDIS_ENCODING_INTSET 6 /* Encoded as intset */
#define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
IV、refcount属性
Redis中为了更好的优化内存空间,对数字字符串进行了共享内存的操作,并以引用计数方式进行管理。
下面的函数为增加引用及减少应用的操作:
// 增加 Redis 对象引用
void incrRefCount(robj *o) {
o->refcount++;
}
// 减少 Redis 对象引用。需要判断是否需要进行析构
void decrRefCount(robj *o) {
if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
// 如果取消的是最后一个引用,则释放资源
if (o->refcount == 1) {
// 不同数据类型,销毁操作不同
switch(o->type) {
case REDIS_STRING: freeStringObject(o); break;
case REDIS_LIST: freeListObject(o); break;
case REDIS_SET: freeSetObject(o); break;
case REDIS_ZSET: freeZsetObject(o); break;
case REDIS_HASH: freeHashObject(o); break;
default: redisPanic("Unknown object type"); break;
}
zfree(o);
} else {
o->refcount--;
}
}
对于这里的引用计数来说,因为Redis是单线程工作模式的,所以引用计数的增加和减少不比保证原子性。
V、lru属性
Redis对数据集占用内存的大小由周期性的计算,当超出限制时,会淘汰超时的数据。即淘汰的标准为:oversize & overtime。
【参考】
[1] 《Redis设计与实现》
[2] 《Redis源码日志》
网友评论