——上一篇文章记录的是Redis最底层用到的主要数据结构,但Redis不是直接以这些数据结构构建键值对数据库的,而是:
- 基于基础数据结构构建出多个对象,从而与Redis提供的数据类型相匹配(字符串、列表、集合、哈希表、有序集合)
对象结构 redisObject
typedef struct redisObject {
unsigned type:4;//类型(对应5种数据类型)
unsigned encoding:4//编码(对应用于底层实现的基本数据结构)
void *ptr;//指向底层实现数据结构的指针
} robj;
一、 字符串对象
可用编码为:
- int ——若对象保存的是整数,且可以用long类型表示,则以保存为int编码。
- raw ——如果对象保存的是字符串值,且长度>32字节,则会使用**SDS(简单动态字符串)**来保存字符串值。raw编码会调用两次内存分配函数来分别创建robj结构和sdshdr结构。
- embstr——如果对象保存的是字符串值,且长度 ≤ 32字节。该方式在raw基础上优化,只调用一次内存分配函数,将robj和sdshdr结构连续存放。(但该种编码方式,是只读的,即若采取了append函数后,底层变为raw编码)
二、列表对象
可用编码为:
- ziplist ——压缩列表(顺序表)
- linkedlist——双端链表,节点值类型是字符串。
三、哈希对象
可用编码为:
- ziplist ——压缩列表,此时同一键值对的两个节点总是紧挨在一起,key在前,value在后。【此时不能用计算哈希值来快速得到索引,性能不会慢吗?大概是压缩列表适用于结点数较少情况下,所以性能不会说太慢?】
- hashtable——使用字典来作为哈希对象底层实现。哈希表的键值对与用户所存的键值对是一致的。
四、集合对象
可用编码为:
- intset ——整数集合(条件,所有元素都是整数值,元素数量不超过512个)
- hashtable——字典。此时字典的键存储所需保存的元素,值则为null。【大概是使用hashtable后,每次存储元素需要计算哈希值,找到对应的索引位置,因此很方便去重】
五、有序集合
可用编码为:
- ziplist ——第一个结点保存元素成员,第二个节点保存元素的分值。
- skiplist ——使用的是zset结构作为底层实现(包含跳跃表zskiplist,字典dict);跳跃表用于查找范围值,字典用于快速获取某个元素的分值。
typedef struct zset {
zskiplist *zsl;//跳跃表,用于范围型操作。
dict *dict;//字典,存储的是成员到分值的映射
}zset;//虽然采取两个数据结构来构建zset,但会通过指针共享相同元素的成员和分值,不会浪费内存。
六、内存回收
- 采取的是引用计数法
- 创建一个新对象时,引用计数值初始化为1;
- 当对象被一个新程序使用时,计数值自增1;
- 当对象不再被一个程序使用时,计数值自减1;
- 计数值变为0时,对象占用内存会被释放。
七、对象共享
- 目前,Redis会在初始化服务器时,创建一万个字符串对象,包含了0~9999的所有整数值。