Redis的每种对象其实都由对象结构(redisObject) 与 对应编码的数据结构组合而成,而每种对象类型对应若干编码方式,不同的编码方式所对应的底层数据结构是不同的
个人理解:
redisObject就类似java集合中List接口是个集合规范,具体用什么实现要看实现类是谁(ArrayList,LinkedList等),同样都有增加、删除操作,但是具体的实现方式都不一样,底层的数据结构也不一样
redisObject
是 Redis 类型系统的核心, 数据库中的每个键、值, 以及 Redis 本身处理的参数, 都表示为这种数据类型.
/*
* Redis 对象
*/
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码方式
unsigned encoding:4;
// LRU - 24位, 记录最末一次访问时间(相对于lru_clock); 或者 LFU(最少使用的数据:8位频率,16位访问时间)
unsigned lru:LRU_BITS; // LRU_BITS: 24
// 引用计数
int refcount;
// 指向底层数据结构实例
void *ptr;
} robj;
下图对应上面的结构
其中type、encoding和ptr是最重要的三个属性。
/*
* 对象类型
*/
#define OBJ_STRING 0 // 字符串
#define OBJ_LIST 1 // 列表
#define OBJ_SET 2 // 集合
#define OBJ_ZSET 3 // 有序集
#define OBJ_HASH 4 // 哈希表
@pdai: 代码已经复制到剪贴板
/*
* 对象编码
*/
#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 /* 注意:版本2.6后不再使用. */
#define OBJ_ENCODING_LINKEDLIST 4 /* 注意:不再使用了,旧版本2.x中String的底层之一. */
#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 */
OBJ_LIST
, encoding 属性为OBJ_ENCODING_QUICKLIST
,那么这个对象就是一个Redis 列表(List),它的值保存在一个QuickList的数据结构内,而ptr 指针就指向quicklist的对象;❓ 为什么Redis会设计redisObject对象?
在redis的命令中,对于**键所保存的值的类型不同(键的类型如string,list,set等),键能执行的命令又各不相同**。
LPUSH
和 LLEN
只能用于列表键, 而 SADD
和 SRANDMEMBER
只能用于集合键, 等等DEL
、 TTL
和 TYPE
, 可以用于任何类型的键;是要正确实现这些命令, 必须为不同类型的键设置不同的处理方式: 比如说, 删除一个列表键和删除一个字符串键的操作过程就不太一样。综上所述, Redis 必须让每个键都带有类型信息, 使得程序可以检查键的类型, 并为它选择适合的处理方式.
比如说, 集合类型就可以由字典和整数集合两种不同的数据结构实现, 但是, 当用户执行 ZADD 命令时, 他/她应该不必关心集合使用的是什么编码, 只要 Redis 能按照 ZADD 命令的指示, 将新元素添加到集合就可以了。
这说明, 操作数据类型的命令除了要对键的类型进行检查之外, 还需要根据数据类型的不同编码进行多态处理.
那么Redis是如何处理一条命令的呢?
当执行一个处理数据类型命令的时候,redis执行以下步骤
type
属性和执行命令所需的类型是否相符,如果不相符,返回类型错误;(判断执行命令和type
属性是否对应)encoding
属性所指定的编码,选择合适的操作函数来处理底层的数据结构;(根据encoding
和选择合适的操作函数执行命令)redis一般会把一些常见的值放到一个共享对象中,可使程序避免了重复分配的麻烦,也降低了CPU消耗
redis预分配的值对象如下:
10000
)❗️TIP:
共享对象只能被字典和双向链表这类能带有指针的数据结构使用
为什么redis不共享列表对象、哈希对象、集合对象、有序集合对象,只共享字符串对象?
列表对象、哈希对象、集合对象、有序集合对象,本身可以包含字符串对象,复杂度较高。对复杂度较高的对象创建共享对象,需要消耗很大的CPU,是不合适的
redisObject中有refcount
属性,是对象的引用计数,显然计数0那么就是可以回收。