redis笔记-对象系统篇

2018-1-4 by Atlas

* 简述

  • redis并无直接使用SDS、链表、字典、压缩列表、整数集合、跳跃表这些数据结构来实现键值对数据库,而是基于这些数据结构建立了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象、有序集合对象这五种类型的对象,每种对象都至少用到了一种上面提到的数据结构。
  • 使用对象的一个好处是,能够针对不一样的使用场景,为对象设置多种不一样的数据结构实现,从而优化对象在不一样场景下的使用效率。
  • 对象系统还实现了基于引用计数技术的内存回收机制,释放再也不使用的对象所占用的内存。
  • 引用计数技术实现了对象共享机制,经过多个数据库键共享同一个对象来节约内存。

* 定义

typeof struct redisObject {
        // 类型
        unsigned type:4;
        // 编码
        unsigned encoding:4;
        // 指向底层实现数据结构的指针
        void *ptr;
        // 引用计数
        int refcount;
        // 空转时长
        unsigned lru:22;
} robj;

* 类型

对象的类型

TYPE命令,返回的结果为数据库键对应的值对象的类型,而不是键对象的类型。redis

redis> SET msg "hello"
ok
redis> TYPE msg
string

不一样类型值对象的TYPE命令输出

* 编码

对象的编码

OBJECT ENCODING命令查看数据库键的值对象的编码。算法

redis> SET msg "hello"
ok
redis> OBJECT ENCODING msg
"embstr"

不一样类型和编码的对象

OBJECT ENCODING对不一样编码的输出

* 类型检查

类型检查是经过redisObject结构的type属性来实现的:
1)执行命令前,服务器先检查输入数据库键的值对象是否为执行命令所需的类型,若是是,服务器就对键执行命令;
2)不然,服务器拒绝执行命令,并向客户端返回一个类型错误。数据库

LLEN命令执行时的类型检查过程
LLEN命令执行时的类型检查过程缓存

* 多态实现

  • 基于编码的多态--一个命令能够同时用于处理多种不一样的编码。
  • 基于类型的多态--一个命令能够同时用于处理多种不一样类型的键。

LLEN命令执行过程
LLEN命令执行过程服务器

* 空转时长

lru属性记录了对象最后一次被命令程序访问的时间。
OBJECT IDLETIME 命令能够打印给定键的空转时长,等于当前时间减去键的值对象的lru时间。
若是服务器打开了maxmemory选项,而且内存回收算法为volatile-lru或者allkeys-lru,那么服务器占用的内存数超过maxmemory选项设置的上限空转时长较高的部分键会优先被释放,回收内存。数据结构

* 内存回收

对象系统构建了一个引用计数技术实现的内存回收机制。
对象的引用计数信息会随着对象的使用状态而不断变化:ide

  • 建立一个新对象时,引用计数值初始化为1;
  • 当对象被一个新程序使用时,引用计数值增1;
  • 当对象再也不被一个程序使用时,引用计数值减1;
  • 当对象的引用计数值变为0,对象所占的内存将会被释放。

* 对象共享

多个键共享同一个值对象须要执行如下步骤:
1)将数据库键的值指针指向一个现有的值对象;
2)将被共享的值对象的引用技术增一。优化

共享对象

* 字符串对象

编码能够是int、raw或者embstr。编码

  • 结构

若是字符串对象保存的是整数值,int编码实现。设计

int编码的字符串对象

若是字符串对象保存的是字符串值,而且长度小于等于39字节,embstr编码实现。

embstr编码的字符串对象

若是字符串对象保存的是字符串值,而且长度大于39字节,raw编码实现。

raw编码的字符串对象

  • 策略

int、embstr、raw三种不一样编码实现字符串对象,就是根据值对象类型优化内存分配。
embstr编码的字符串对象较raw编码的字符串对象将内存分配次数从两次下降为一次。
embstr编码的字符串对象较raw编码的字符串对象内存释放也从两次减小为一次。
embstr编码的字符串对象数据保存在一块连续的内存可以更好地利用缓存的优点。
可是embstr编码的字符串对象其实是只读的,int编码的字符串对象的值变动成字符串值,对象将转换成raw编码的字符串对象。

  • 命令

SET:保存值。
GET:取值。
APPEND:追加字符串。
STRLEN:取字符串长度。

* 列表对象

编码能够是ziplist或者linkedlist。

  • 结构

ziplist编码列表对象。

ziplist编码列表对象

linkedlist编码列表对象。

linkedlist编码列表对象

  • 策略

同时知足如下两个条件时,列表对象使用ziplist编码:
1)列表对象保存的全部字符串元素的长度都小于64字节;
2)列表对象保存的元素数量小于512个。
任意一个条件不知足,列表对象使用linkedlist编码。
配置:list-max-ziplist-value选项和list-max-ziplist-entries选项。

  • 命令

RPUSH:添加列表表头元素。
LPOP:弹出列表表头元素。
LINSERT:插入元素。
LLEN:取列表长度。

* 哈希对象

编码能够是ziplist或者hashtable。

  • 结构

ziplist编码哈希对象

ziplist编码哈希对象

hashtable编码哈希对象

hashtable编码哈希对象

  • 策略

同时知足如下两个条件,哈希对象使用ziplist编码:
1)哈希对象保存的全部键值对对象的键和值的字符串长度都小于64字节;
2)哈希对象保存的键值对数量小于512个。
任意一个条件不知足,哈希对象使用hashtable编码。
配置:hash-max-ziplist-value选项和hash-max-ziplist-entries选项。

  • 命令

HSET:添加元素。
HGET:获取元素。
HDEL:删除元素。
HLEN:取哈希元素数量

* 集合对象

编码能够是intset或者hashtable。

  • 结构

intset编码集合对象。

intset编码集合对象

hashtable编码集合对象。

hashtable编码集合对象

  • 策略

同时知足如下两个条件,集合对象使用intset编码:
1)集合对象保存的全部元素都是整数值;
2)集合对象保存的元素数量不超过512个。
任意一个条件不知足,集合对象使用hashtable编码。

  • 命令

SADD:添加元素。
SPOP:弹出元素。
SCARD:取集合元素数量。

* 有序集合对象

编码能够是ziplist或者skiplist。

  • 结构

ziplist编码有序集合对象。

ziplist编码有序集合对象

skiplist编码有序集合对象。

skiplist编码有序集合对象

  • 策略

同时知足如下两个条件,有序集合对象使用ziplist编码:
1)有序集合对象保存的全部成员的长度都小于64字节;
2)有序集合对象保存的元素数量小于128个。
任意一个条件不知足,序集合对象使用skiplist编码。

  • 命令

ZADD:添加元素。
ZCARD:取有序集合对象元素数量。
ZRANK:从表头向表尾遍历有序集合。
ZSCORE:取给定成员分值。

参考文献:《redis设计与实现》

相关文章
相关标签/搜索