redis 对象
codeflysafe Lv5

Untitled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct redisObject {
// 类型 主要是五种 string、list、 hash、set、zset
unsigned type:4;
// 编码 对象所使用的编码
unsigned encoding:4;
// lru
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */

// 引用计数个数
int refcount;

// 指向底层数据库对象
void *ptr;
} robj;

redisObject 对象结构,从上图可以清晰的看出,下面记录一下相同对象类型的对象编码是如何切换的

String 字符串对象

Untitled

embstr 编码格式字符串是SDS结构的优化编码方式,它采用一次内存分配,将 redisObject 和 sdshdr 结构 同时分配一块连续的空间。

Untitled

浮点数(long double )也是保存为字符串形式,执行加法等操作时,会将它先转为浮点数操作,后面在转为字符串存储

编码的转换

Untitled

状态转换图如上

**int → raw**

1
2
3
4
5
6
7
8
127.0.0.1:6379> set number 123
OK
127.0.0.1:6379> object encoding number
"int"
127.0.0.1:6379> append number 4
(integer) 4
127.0.0.1:6379> object encoding number
"raw"

**embstr → raw**

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> set msg "hello world"
OK
127.0.0.1:6379> object encoding msg
"embstr"
127.0.0.1:6379> append 1
(error) ERR wrong number of arguments for 'append' command
127.0.0.1:6379> append msg 1
(integer) 12
127.0.0.1:6379> object encoding msg
"raw"

list 对象

Untitled

转换一样是破坏上面那两个条件

但是新版的redis list 只有 quicklist, 下面去学习一下

哈希对象

Untitled

转换一样是破坏上面那两个条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> hset prifile name "Tom"
(integer) 1
127.0.0.1:6379> hset prifile age 25
(integer) 1
127.0.0.1:6379> hset prifile career "Progresser"
(integer) 1
127.0.0.1:6379> object encoding prifile
"ziplist"
127.0.0.1:6379> hset prifile time "11111111111111111111111111111111111111111111111111111111111111111111""
Invalid argument(s)
127.0.0.1:6379> hset prifile time "11111111111111111111111111111111111111111111111111111111111111111111"
(integer) 1
127.0.0.1:6379> object encoding prifile
"hashtable"
127.0.0.1:6379>

集合对象

无序的( 虽说intset 是有序的,但是集合本身是无序的)

编码转换

Untitled

破环上面的条件,可以完成编码转换

有序集合对象

1
2
3
4
typedef struct zset {
dict *dict;
zskiplist *zsl;
} zset;

有序集合的编码可以使用 ziplist 或者 skiplist

使用 ziplist ,是将其按照 score 大小从小到大排列

而 使用skiplist, 则是使用 zet(由skiplist和dict一起实现)

zet 使用 skiplist 和 dict 的原因

  1. 可以兼顾两者的有点
  2. 在O(1)时间内可以得到元素的score, 又可以实现范围查询操作 ZRANGE

编码的转换

Untitled

类型检查和多态

类型检查

使用 object的 type 进行类型检查

1
2
3
4
5
void llenCommand(redisClient *c) {
robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
addReplyLongLong(c,listTypeLength(o));
}

多态

使用object的 encoding 进行区分底层数据结构,来实现多态

1
2
3
4
5
6
7
8
9
unsigned long listTypeLength(robj *subject) {
if (subject->encoding == REDIS_ENCODING_ZIPLIST) {
return ziplistLen(subject->ptr);
} else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) {
return listLength((list*)subject->ptr);
} else {
redisPanic("Unknown list encoding");
}
}

内存回收

采用的是引用技术技术实现的内存回收机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct redisObject {
// 类型 主要是五种 string、list、 hash、set、zset
unsigned type:4;
// 编码 对象所使用的编码
unsigned encoding:4;
// lru
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */

// 引用计数个数
int refcount;

// 指向底层数据库对象
void *ptr;
} robj;

引用计数技术的GC,比较简单,放到GC里面学习,这里不过多介绍

唯一值得介绍的是,回收是在引用个数减为0的时候回收的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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使用了对象共享计数。一个简单的例子,假设健A和健B均包换整数值100,如果建立一个100的字符串作为值对象,让健共享这个值对象,就可以更加节约内存

如何相同值越多,采用对象共享机制,节约的内存也越多

Redis 在初始化时,已经缓存了0~9999的所有整数值。不共享字符串的原因是,验证操作时间复杂度为O(N),

1
2
3
4
for (j = 0; j < REDIS_SHARED_INTEGERS; j++) {
shared.integers[j] = createObject(REDIS_STRING,(void*)(long)j);
shared.integers[j]->encoding = REDIS_ENCODING_INT;
}
  • 本文标题:redis 对象
  • 本文作者:codeflysafe
  • 创建时间:2022-03-02 16:31:01
  • 本文链接:https://codeflysafe.github.io/2022/03/02/redis-object/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论