这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
一、对象介绍及特性
1、对象简介
2、对象的类型
3、对象的编码和底层实现
1、对象简介
在前面的几遍中,我们学习了Redis用到的所有主要数据结构,比如简单动态字符串(SDS)、双端链表、字典、压缩列表、跳跃表、整数集合。
Redis并没有使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这个系统包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象,每种对象都用到了至少一种我们前面所介绍的数据结构。
通过这五种不同类型的对象,Redis可以在执行命令之前,根据对象的类型来判断一个对象是否可以执行给定的命令。使用对象的另一个好处是,我们可以针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。
Redis的对象系统还实现了基于引用计数技术的内存回收机制,当程序不使用某个对象的时候,这个对象所占用的内存就会被自动释放;另外,Redis还通过引用计数器技术实现了对象共享机制,这一机制可以在适当的条件下,通过让多个数据库键共享同一个对象来节约内存。
Redis的对象带有访问时间记录信息,该信息可以用于计算数据库键的空转时长,在服务器启用了maxmemory功能的情况下,空转时长较大的那些键可能会优先被服务器删除。
2、对象类型
Redis使用对象来表示数据库中的键和值,每次新建一个键值对时,至少会创建两个对象,一个对象用作键值对的键(键对象),另一个对象用作键值对的值(值对象)。 Redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type属性、encoding属性和ptr属性。
typedef struct redisObject {
//类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 指向底层实现数据结构的指针
void *ptr;
// ....
} robj;
对象的type属性记录了对象的类型,这个属性的值可以是下表列出的常量的其中一个。
| 类型常量 | 对象的名称 |
|---|---|
| REDIS_STRING | 字符串对象 |
| REDIS_LIST | 列表对象 |
| REDIS_HASH | 哈希对象 |
| REDIS_SET | 集合对象 |
| REDIS_ZSET | 有序集合对象 |
对于Redis数据库保存的键值对来说,键总是一个字符串对象,而值则可以是上表中的任何一种。
当我们要查看一个数据库键的类型时,可以使用TYPE命令。
# 键为字符串对象,值为字符串对象
redis> SET msg "hello world"
OK
redis> TYPE msg
string
3、对象的编码和底层实现
对象的ptr指针指向对象的底层实现数据结构,而这些数据结构由对象的encoding属性决定。
encoding属性记录了对象所使用的编码,也就是说这个对象使用了什么数据结构作 为对象的底层实现,这个属性的值可以是下表中的任何一个。
| 编码常量 | 编码所对应的底层数据结构 |
|---|---|
| REDIS_ENCODING_INT | long类型的整数 |
| REDIS_ENCODING_EMBSTR | embstr编码的简单动态字符串 |
| REDIS_ENCODING_RAW | 简单动态字符串 |
| REDIS_ENCODING_HT | 字典 |
| REDIS_ENCODING_LINKEDLIST | 双端链表 |
| REDIS_ENCODING_ZIPLIST | 压缩列表 |
| REDIS_ENCODING_INTSET | 整数集合 |
| REDIS_ENCODING_SKIPLIST | 跳跃表和字典 |
每种类型的对象都至少使用了两种不同的编码,下表列出了每种类型的对象可以使用的编码。
| 类型 | 编码 | 对象 |
|---|---|---|
| REDIS_STRING | REDIS_ENCODING_INT | 使用整数值实现的字符串对象 |
| REDIS_STRING | REDIS_ENCODING_EMBSTR | 使用embstr编码的简单动态字符串实现的字符串对象 |
| REDIS_STRING | REDIS_ENCODING_RAW | 使用简单动态字符串实现的字符串对象 |
| REDIS_LIST | REDIS_ENCODING_ZIPLIST | 使用压缩列表实现的列表对象 |
| REDIS_LIST | REDIS_ENCODING_LINKEDLIST | 使用双端链表实现的列表对象 |
| REDIS_HASH | REDIS_ENCODING_ZIPLIST | 使用压缩列表实现的哈希对象 |
| REDIS_HASH | REDIS_ENCODING_HT | 使用字典实现的哈希对象 |
| REDIS_SET | REDIS_ENCODING_INTSET | 使用整数集合实现的集合对象 |
| REDIS_SET | REDIS_ENCODING_HT | 使用字典实现的集合对象 |
| REDIS_ZSET | REDIS_ENCODING_ZIPLIST | 使用压缩列表实现的有序集合对象 |
| REDIS_ZSET | REDIS_ENCODING_SKIPLIST | 使用跳跃表和字典实现的有序集合对象 |
使用OBJECT ENCODING命令可以查看一个数据库键的值对象的编码:
redis> SET msg "hello world"
OK
redis> OBJECT ENCODING msg
"embstr"
通过encoding属性来设定对象所使用的编码,而不是为特定类型的对象关联一种固定的编码,极大地提升了Redis的灵活性和效率。
举个例子,在列表对象包含的元素比较少时,Redis使用压缩列表作为列表对象的底层实现:
1、因为压缩列表比双端链表更节约内存,并且在元素数量较少时,在内存中以连续块的方式保存的压缩列表比起双端链表可以更快被载入到缓存中;
2、随着列表对象包含的元素越来越多,使用压缩列表来保存元素的优势逐渐消失时,对象就会将底层实现从压缩列表转向功能更强、也更适合保存大量元素的双端链表上面;