Redis中的对象:
- 字符串对象
- 列表对象——压缩列表或双端链表
- 哈希对象——压缩列表或字典
- 集合对象——整数集合或字典
- 有序集合对象——压缩列表或跳表+字典
每个对象针对不同的使用场景选择底层实现,并使用引用计数器决定对象的回收,某些条件下可以通过引用计数器实现共享。
8.1 对象的类型和编码
Redis中的键和值都是对象,键固定是字符串对象,称某某键指的是值对象。譬如列表键,即值是列表对象。redisObject的结构和对象类型、编码、内存回收、共享对象都有关系,一个对象内存占用 4bit+4bit+24bit+4byte+8byte=16byte
typedef struct redisObject{ //对象类型 unsigned type:4bit; //实现对象的编码方式 unsigned encoding:4bit; //指向底层实现数据结构的指针,在64位系统中,一个指针8字节 void *ptr:8byte; .... //引用计数,int占用4字节 int refcount:4byte; //最后一次被命令程序访问的时间,在2.6版本是22bit,在4.0版本是24bit unsigned lru:22bit; }
8.2 字符串对象
编码方式:
- int编码:字符串对象保存的值是整数值(long)
- raw编码:字符串对象保存的值是字符串,并且长度大于39字节,使用SDS实现
- embstr编码:针对段字符串的优化,减少分配和释放需要的内存分配次数
raw编码和embstr编码也可以保存浮点数
字符串对象大小不能超过512M
8.2.1 编码的转换
int编码的值被修改成字符串、embstr编码的值一旦被修改(例如APPEND操作),都会被转换成raw编码方式。
8.3 列表对象
redis> RPUSH numbers 1 "three" 5
编码方式:
- ziplist编码:底层使用压缩列表实现
- linkedist编码:使用双端列表实现
当列表对象保存的所有字符串元素的长度小于64字节,且对象保存的元素数量小于512个时,使用ziplist编码,否则使用linkedlist编码
8.4 哈希对象
编码方式:
- ziplist编码:使用压缩列表实现。当有新键值对插入时,先将键的压缩列表节点推入列表的尾部,再将值的压缩列表节点推入列表的尾部。
- hashtable编码:使用字典实现。
当哈希对象保存的所有键值对的字符串都小于64字节,并且键值对的数量小于512个,使用ziplist编码,否则使用hashtable编码
8.5 集合对象
redis>SADD numbers 1 3 5
编码方式:
- intset编码:使用整数集合实现。
- hashtable编码:使用字典实现。字典的每个键都是字符串对象,保存了一个集合的值,字典的值全部设置为NULL。
当保存的集合的元素都是整数,并且总数不超过512个时,使用intset编码,否则使用hashtable编码
8.6 有序集合对象
编码方式:
- ziplist编码:每个元素使用两个压缩列表节点表示,第一个存放元素的值,第二个存放元素的分值。元素按照分值大小从小到大排序,分值越大越靠近表尾。
- skiplist编码:使用跳表+字典实现。
在跳表的实现中,每个节点的object保存了元素的值,score保存了分值。使用ZRANK和ZRANGE等范围操作可以有效的使用跳表排序的特性。
在字典的实现中,字典键存放元素的值,字典值存放元素的分数。查找每个元素的分值的复杂度是常量级。
同时使用跳表和字典实现,会用指针来共享元素的成员和分值,所以不会造成额外内存的浪费。还能结合二者的优势。
当保存的集合的元素数量小于128个,而且所有元素的长度都小于64字节时,使用ziplist编码,否则使用skiplist实现。
8.7 类型检查和命令多态
- 类型检查:不同的命令针对不同的对象,Redis在执行特定命令时,会通过对象中的type属性检查输入键的类型是否正确。
- 多态命令:同一个命令的目标对象,底层的实现不同,调用的API也不同,Redis通过encoding属性得知具体的底层实现,调用不同的API。
8.8 内存回收
对象的refcount属性记录了引用关系
- 创建新对象时,引用计数器初始化成1
- 当对象被一个新的程序使用时,计数器值+1
- 当对象不再被某个程序使用时,计数器值-1
- 当对象的引用计数器值变成0,对象占用的内存被释放
8.9 对象共享
当一个对象被多个程序使用时,譬如键A的值对象和键B的值对象相同时,只会创建一个对象,A和B的值对象都指向该对象,同时该对象的引用计数器记为2。受到CPU时间的限制,Redis只对包含整数值的字符串对象进行共享。目前Redis会共享0~9999的字符串对象。
8.10 对象的空转时常
对象的lru属性,记录了对象最后一次被命令程序访问的时间。OBJECT IDLETIME命令可以打印出给定键的lru属性,并且不会更改lru属性。