一般来说redis中的每种数据都是创建了一个key,value键值对对象来存储,key必定为字符串,而value才为笔记(一)中的数据类型。
数据对象的结构体定义
typedef struct redisObject { // 类型 unsigned type:4; // 编码 unsigned encoding:4; // 对象最后一次被访问的时间(日后会说) unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */ // 引用计数 int refcount; // 指向实际值的指针 void *ptr; } robj;
引用计数:
因为 C 语言并不具备自动的内存回收功能,所以 Redis 在自己的对象系统中构建了一个引用计数(reference counting)技术实现的内存回收机制,通过这一机制,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收。
对象的引用计数信息会随着对象的使用状态而不断变化:
·在创建一个新对象时, 引用计数的值会被初始化为1;
·当对象被一个新程序使用时,它的引用计数值会被增一;
·当对象不再被一个程序使用时,它的引用计数值会被减一;
·当对象的引用计数值变为0时,对象所占用的内存会被释放
除了用于实现引用计数内存回收机制之外,对象的引用计数属性还带有对象共享(只对整数值)的作用。假设键A创建了一个包含整数值100的字符串对象作为值对象:
为什么共享对象只对整数值,而不共享包含字符串的对象呢?
因为需要有一个验证时间!只有当共享目标和想要建立的键一样时,才会共享,于是整数只需要O(1),字符串需要O(n),列表或hash则需要O(n^2),这将造成CPU资源浪费。
类型检查和多态
Redis中用于操作键的命令基本分为两种,一种是对任何类型键进行操作,比如说 DEL 命令、 EXPIRE 命令、 RENAME 命令、 TYPE 命令、 OBJECT 命令等。另一种是对特定类型的键操作,如SET 、 GET 、 APPEND 、 STRLEN 等命令只能对字符串键执行。
类型检查
在执行一个类型特定的命令之前, Redis 会先检查输入键的类型是否正确, 然后再决定是否执行给定的命令。
类型特定命令所进行的类型检查是通过 redisObject 结构的 type 属性来实现的:
多态命令
这里指的是同一个对象一般具有两种编码方式,比如hash对象是hashtable和ziplist,那么执行命令的时候是怎么确定的呢
字符串对象
字符串对象的编码可以是int、raw、embstr。
int: 如果字符串对象的整数值可以被long类型表示,那么该整数值会被保存在ptr指针中,并设置编码格式为int
embstr: 如果该保存的字符串小于等于39,那就使用embstr编码。embstr编码一次内存分配一块连续的内存空间。另外embstr编码方式的字符串不可修改,只可读。若对一个embstr编码的字符串执行修改操作,则这个字符串必定会转换成raw编码的字符串。
raw: 如果字符串对象保存的是字符串,且大于39字节,那就使用SDS字符串保存,编码格式为raw,raw编码调用两次内存分配来分别存储redisobject和sdshdr。
常用函数:
注: 字符串对象是 Redis 五种类型的对象中唯一一种会被其他四种类型对象嵌套的对象。
列表对象
其底层是ziplist或是linkedlist。
常用函数
哈希对象(hash)
底层是ziplist或者hashtable。
底层用hashtable时,键和值都是字符串对象。
常用函数:
集合对象(set)
集合对象的编码可以是intset或者hashtable 。
使用intset的条件是集合对象保存的所有元素都是整数值。而当使用hashtable时,底层是一个字典实现,键是一个字符串对象,值是NULL。
常用函数:
有序集合对象(zset)
有序集合的编码可以是ziplist+字典或者skiplist+字典。
常用函数: