Redis设计与实现 第一部分

1、数据结构与实现

1.简单动态字符串

在C语言中,字符串是以字符数组的形式体现的(以’\0’为结束符),html

Redis中的字符串定义以下:node

/* 字符串结构体(字符串就是字符数组) */
struct sdshdr {
    // 字符串当前长度
    unsigned int len;
    // 剩余可用长度
    unsigned int free;
    // 字符数组(具体存放字符串的地方)
    char buf[];
};
  • Redis 只会使用 C 字符串做为字面量, 在大多数状况下, Redis 使用 SDS (Simple Dynamic String,简单动态字符串)做为字符串表示。
  • 比起 C 字符串, SDS 具备如下优势:
    1. 常数复杂度获取字符串长度。
    2. 杜绝缓冲区溢出。
    3. 减小修改字符串长度时所需的内存重分配次数。
    4. 二进制安全。
    5. 兼容部分 C 字符串函数。

2.链表

typedef struct list {
    // 表头节点
    listNode *head;
    // 表尾节点
    listNode *tail;
    // 链表所包含的节点数量
    unsigned long len;
    // 节点值复制函数
    void *(*dup)(void *ptr);
    // 节点值释放函数
    void (*free)(void *ptr);
    // 节点值对比函数
    int (*match)(void *ptr, void *key);
} list;
  • 链表被普遍用于实现 Redis 的各类功能, 好比列表键, 发布与订阅, 慢查询, 监视器, 等等。
  • 每一个链表节点由一个listnode 结构来表示, 每一个节点都有一个指向前置节点和后置节点的指针, 因此 Redis 的链表实现是双端链表。
  • 每一个链表使用一个list结构来表示, 这个结构带有表头节点指针、表尾节点指针、以及链表长度等信息。
  • 由于链表表头节点的前置节点和表尾节点的后置节点都指向NULL, 因此 Redis 的链表实现是无环链表。
  • 经过为链表设置不一样的类型特定函数, Redis 的链表能够用于保存各类不一样类型的值。

3. 字典

哈希表redis

typedef struct dictht {
    // 哈希表数组
    dictEntry **table;
    // 哈希表大小
    unsigned long size;
    // 哈希表大小掩码,用于计算索引值
    // 老是等于 size - 1
    unsigned long sizemask;
    // 该哈希表已有节点的数量
    unsigned long used;
} dictht;

哈希表节点 算法

typedef struct dictEntry {
    // 键
    void *key;
    // 值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;
    // 指向下个哈希表节点,造成链表
    struct dictEntry *next;
} dictEntry;

字典数据库

typedef struct dict {
    // 类型特定函数
    dictType *type;
    // 私有数据
    void *privdata;
    // 哈希表
    dictht ht[2];
    // rehash 索引
    // 当 rehash 不在进行时,值为 -1
    int rehashidx; /* rehashing not in progress if rehashidx == -1 */
} dict;

dictType数组

typedef struct dictType {
    // 计算哈希值的函数
    unsigned int (*hashFunction)(const void *key);
    // 复制键的函数
    void *(*keyDup)(void *privdata, const void *key);
    // 复制值的函数
    void *(*valDup)(void *privdata, const void *obj);
    // 对比键的函数
    int (*keyCompare)(void *privdata, const void *key1, const void *key2);
    // 销毁键的函数
    void (*keyDestructor)(void *privdata, void *key);
    // 销毁值的函数
    void (*valDestructor)(void *privdata, void *obj);
} dictType;

当如下条件中的任意一个被知足时, 程序会自动开始对哈希表执行扩展操做:安全

# 负载因子 = 哈希表已保存节点数量 / 哈希表大小
load_factor = ht[0].used / ht[0].size
  1. 服务器目前没有在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 而且哈希表的负载因子大于等于 1
  2. 服务器目前正在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 而且哈希表的负载因子大于等于 5
  3. 当哈希表的负载因子小于 0.1 时, 程序自动开始对哈希表执行收缩操做。

重点回顾服务器

  1. 字典被普遍用于实现 Redis 的各类功能, 其中包括数据库和哈希键。
  2. Redis 中的字典使用哈希表做为底层实现, 每一个字典带有两个哈希表, 一个用于平时使用, 另外一个仅在进行 rehash 时使用。
  3. 当字典被用做数据库的底层实现, 或者哈希键的底层实现时, Redis 使用 MurmurHash2 算法来计算键的哈希值。
  4. 哈希表使用链地址法来解决键冲突, 被分配到同一个索引上的多个键值对会链接成一个单向链表。
  5. 在对哈希表进行扩展或者收缩操做时, 程序须要将现有哈希表包含的全部键值对 rehash 到新哈希表里面, 而且这个 rehash 过程并非一次性地完成的, 而是渐进式地完成的。

4.跳跃表

跳跃表节点的实现由 redis.h/zskiplistNode 结构定义:数据结构

typedef struct zskiplistNode {
    // 后退指针
    struct zskiplistNode *backward;
    // 分值
    double score;
    // 成员对象
    robj *obj;
    // 层
    struct zskiplistLevel {
        // 前进指针
        struct zskiplistNode *forward;
        // 跨度
        unsigned int span;
    } level[];
} zskiplistNode;

zskiplist 结构的定义以下:dom

typedef struct zskiplist {
    // 表头节点和表尾节点
    struct zskiplistNode *header, *tail;
    // 表中节点的数量
    unsigned long length;
    // 表中层数最大的节点的层数
    int level;
} zskiplist;

header 和 tail 指针分别指向跳跃表的表头和表尾节点, 经过这两个指针, 程序定位表头节点和表尾节点的复杂度为 O(1) 。

经过使用 length 属性来记录节点的数量, 程序能够在 O(1) 复杂度内返回跳跃表的长度。

level 属性则用于在 O(1) 复杂度内获取跳跃表中层高最大的那个节点的层数量, 注意表头节点的层高并不计算在内。

重点回顾

  • 跳跃表是有序集合的底层实现之一, 除此以外它在 Redis 中没有其余应用。
  • Redis 的跳跃表实现由 zskiplist 和 zskiplistNode 两个结构组成, 其中 zskiplist 用于保存跳跃表信息(好比表头节点、表尾节点、长度), 而 zskiplistNode 则用于表示跳跃表节点。
  • 每一个跳跃表节点的层高都是 1 至 32 之间的随机数。
  • 在同一个跳跃表中, 多个节点能够包含相同的分值, 但每一个节点的成员对象必须是惟一的。
  • 跳跃表中的节点按照分值大小进行排序, 当分值相同时, 节点按照成员对象的大小进行排序。

5.整数集合

整数集合(intset)是 Redis 用于保存整数值的集合抽象数据结构, 它能够保存类型为 int16_t 、 int32_t 或者 int64_t 的整数值, 而且保证集合中不会出现重复元素

typedef struct intset {
    // 编码方式
    uint32_t encoding;
    // 集合包含的元素数量
    uint32_t length;
    // 保存元素的数组
    int8_t contents[];
} intset;

重点回顾

  • 整数集合是集合键的底层实现之一。
  • 整数集合的底层实现为数组, 这个数组以有序、无重复的方式保存集合元素, 在有须要时, 程序会根据新添加元素的类型, 改变这个数组的类型。
  • 升级操做为整数集合带来了操做上的灵活性, 而且尽量地节约了内存。
  • 整数集合只支持升级操做, 不支持降级操做。

6.压缩列表

digraph {

    label = "\n 图 7-1    压缩列表的各个组成部分";

    node [shape = record];

    ziplist [label = " zlbytes | zltail | zllen | entry1 | entry2 | ... | entryN | zlend "];

}

压缩列表各个组成部分的详细说明

属性 类型 长度 用途
zlbytes uint32_t 4 字节 记录整个压缩列表占用的内存字节数:在对压缩列表进行内存重分配, 或者计算 zlend的位置时使用。
zltail uint32_t 4 字节 记录压缩列表表尾节点距离压缩列表的起始地址有多少字节: 经过这个偏移量,程序无须遍历整个压缩列表就能够肯定表尾节点的地址。
zllen uint16_t 2 字节 记录了压缩列表包含的节点数量: 当这个属性的值小于 UINT16_MAX (65535)时, 这个属性的值就是压缩列表包含节点的数量; 当这个值等于 UINT16_MAX 时, 节点的真实数量须要遍历整个压缩列表才能计算得出。
entryX 列表节点 不定 压缩列表包含的各个节点,节点的长度由节点保存的内容决定。
zlend uint8_t 1 字节 特殊值 0xFF (十进制 255 ),用于标记压缩列表的末端。

每一个压缩列表节点都由 previous_entry_length 、 encoding 、 content 三个部分组成, 如图 :

digraph {

    label = "\n 图 7-4    压缩列表节点的各个组成部分";

    node [shape = record];

    n [label = " previous_entry_length | encoding | content "];

}

 每一个节点的 previous_entry_length 属性都记录了前一个节点的长度:

  • 若是前一节点的长度小于 254 字节, 那么 previous_entry_length 属性须要用 1 字节长的空间来保存这个长度值。
  • 若是前一节点的长度大于等于 254 字节, 那么 previous_entry_length 属性须要用 5 字节长的空间来保存这个长度值。

重点回顾

  • 压缩列表是一种为节约内存而开发的顺序型数据结构。
  • 压缩列表被用做列表键和哈希键的底层实现之一。
  • 压缩列表能够包含多个节点,每一个节点能够保存一个字节数组或者整数值。
  • 添加新节点到压缩列表, 或者从压缩列表中删除节点, 可能会引起连锁更新操做, 但这种操做出现的概率并不高。

7.对象

readobject

typedef struct redisObject {
    // 类型
    unsigned type:4;
    // 编码
    unsigned encoding:4;
    // 指向底层实现数据结构的指针
    void *ptr;
    // ...
} robj;

对象的编码

编码常量 编码所对应的底层数据结构
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 对不一样编码的输出

对象所使用的底层数据结构 编码常量 OBJECT ENCODING 命令输出
整数 REDIS_ENCODING_INT "int"
embstr 编码的简单动态字符串(SDS) REDIS_ENCODING_EMBSTR "embstr"
简单动态字符串 REDIS_ENCODING_RAW "raw"
字典 REDIS_ENCODING_HT "hashtable"
双端链表 REDIS_ENCODING_LINKEDLIST "linkedlist"
压缩列表 REDIS_ENCODING_ZIPLIST "ziplist"
整数集合 REDIS_ENCODING_INTSET "intset"
跳跃表和字典 REDIS_ENCODING_SKIPLIST "skiplist"

(1)字符串对象

字符串对象的编码能够是 int 、 raw 或者 embstr (特殊,短string,不能修改)

命令 int 编码的实现方法 embstr 编码的实现方法 raw 编码的实现方法
SET 使用 int 编码保存值。 使用 embstr 编码保存值。 使用 raw 编码保存值。
GET 拷贝对象所保存的整数值, 将这个拷贝转换成字符串值, 而后向客户端返回这个字符串值。 直接向客户端返回字符串值。 直接向客户端返回字符串值。
APPEND 将对象转换成 raw 编码, 而后按 raw编码的方式执行此操做。 将对象转换成 raw 编码, 而后按 raw编码的方式执行此操做。 调用 sdscatlen 函数, 将给定字符串追加到现有字符串的末尾。
INCRBYFLOAT 取出整数值并将其转换成 longdouble 类型的浮点数, 对这个浮点数进行加法计算, 而后将得出的浮点数结果保存起来。 取出字符串值并尝试将其转换成long double 类型的浮点数, 对这个浮点数进行加法计算, 而后将得出的浮点数结果保存起来。 若是字符串值不能被转换成浮点数, 那么向客户端返回一个错误。 取出字符串值并尝试将其转换成 longdouble 类型的浮点数, 对这个浮点数进行加法计算, 而后将得出的浮点数结果保存起来。 若是字符串值不能被转换成浮点数, 那么向客户端返回一个错误。
INCRBY 对整数值进行加法计算, 得出的计算结果会做为整数被保存起来。 embstr 编码不能执行此命令, 向客户端返回一个错误。 raw 编码不能执行此命令, 向客户端返回一个错误。
DECRBY 对整数值进行减法计算, 得出的计算结果会做为整数被保存起来。 embstr 编码不能执行此命令, 向客户端返回一个错误。 raw 编码不能执行此命令, 向客户端返回一个错误。
STRLEN 拷贝对象所保存的整数值, 将这个拷贝转换成字符串值, 计算并返回这个字符串值的长度。 调用 sdslen 函数, 返回字符串的长度。 调用 sdslen 函数, 返回字符串的长度。
SETRANGE 将对象转换成 raw 编码, 而后按 raw编码的方式执行此命令。 将对象转换成 raw 编码, 而后按 raw编码的方式执行此命令。 将字符串特定索引上的值设置为给定的字符。
GETRANGE 拷贝对象所保存的整数值, 将这个拷贝转换成字符串值, 而后取出并返回字符串指定索引上的字符。 直接取出并返回字符串指定索引上的字符。 直接取出并返回字符串指定索引上的字符。

(2)列表对象

列表对象的编码能够是 ziplist 或者 linkedlis

列表命令的实现

命令 ziplist 编码的实现方法 linkedlist 编码的实现方法
LPUSH 调用 ziplistPush 函数, 将新元素推入到压缩列表的表头。 调用 listAddNodeHead 函数, 将新元素推入到双端链表的表头。
RPUSH 调用 ziplistPush 函数, 将新元素推入到压缩列表的表尾。 调用 listAddNodeTail 函数, 将新元素推入到双端链表的表尾。
LPOP 调用 ziplistIndex 函数定位压缩列表的表头节点, 在向用户返回节点所保存的元素以后, 调用 ziplistDelete 函数删除表头节点。 调用 listFirst 函数定位双端链表的表头节点, 在向用户返回节点所保存的元素以后, 调用 listDelNode 函数删除表头节点。
RPOP 调用 ziplistIndex 函数定位压缩列表的表尾节点, 在向用户返回节点所保存的元素以后, 调用 ziplistDelete 函数删除表尾节点。 调用 listLast 函数定位双端链表的表尾节点, 在向用户返回节点所保存的元素以后, 调用 listDelNode 函数删除表尾节点。
LINDEX 调用 ziplistIndex 函数定位压缩列表中的指定节点, 而后返回节点所保存的元素。 调用 listIndex 函数定位双端链表中的指定节点, 而后返回节点所保存的元素。
LLEN 调用 ziplistLen 函数返回压缩列表的长度。 调用 listLength 函数返回双端链表的长度。
LINSERT 插入新节点到压缩列表的表头或者表尾时, 使用 ziplistPush 函数; 插入新节点到压缩列表的其余位置时, 使用 ziplistInsert 函数。 调用 listInsertNode 函数, 将新节点插入到双端链表的指定位置。
LREM 遍历压缩列表节点, 并调用 ziplistDelete 函数删除包含了给定元素的节点。 遍历双端链表节点, 并调用 listDelNode 函数删除包含了给定元素的节点。
LTRIM 调用 ziplistDeleteRange 函数, 删除压缩列表中全部不在指定索引范围内的节点。 遍历双端链表节点, 并调用 listDelNode 函数删除链表中全部不在指定索引范围内的节点。
LSET 调用 ziplistDelete 函数, 先删除压缩列表指定索引上的现有节点, 而后调用 ziplistInsert 函数, 将一个包含给定元素的新节点插入到相同索引上面。 调用 listIndex 函数, 定位到双端链表指定索引上的节点, 而后经过赋值操做更新节点的值

(3)哈希对象

哈希命令的实现

命令 ziplist 编码实现方法 hashtable 编码的实现方法
HSET 首先调用 ziplistPush 函数, 将键推入到压缩列表的表尾, 而后再次调用 ziplistPush 函数, 将值推入到压缩列表的表尾。 调用 dictAdd 函数, 将新节点添加到字典里面。
HGET 首先调用 ziplistFind 函数, 在压缩列表中查找指定键所对应的节点, 而后调用 ziplistNext 函数, 将指针移动到键节点旁边的值节点, 最后返回值节点。 调用 dictFind 函数, 在字典中查找给定键, 而后调用 dictGetVal 函数, 返回该键所对应的值。
HEXISTS 调用 ziplistFind 函数, 在压缩列表中查找指定键所对应的节点, 若是找到的话说明键值对存在, 没找到的话就说明键值对不存在。 调用 dictFind 函数, 在字典中查找给定键, 若是找到的话说明键值对存在, 没找到的话就说明键值对不存在。
HDEL 调用 ziplistFind 函数, 在压缩列表中查找指定键所对应的节点, 而后将相应的键节点、 以及键节点旁边的值节点都删除掉。 调用 dictDelete 函数, 将指定键所对应的键值对从字典中删除掉。
HLEN 调用 ziplistLen 函数, 取得压缩列表包含节点的总数量, 将这个数量除以 2 , 得出的结果就是压缩列表保存的键值对的数量。 调用 dictSize 函数, 返回字典包含的键值对数量, 这个数量就是哈希对象包含的键值对数量。
HGETALL 遍历整个压缩列表, 用 ziplistGet 函数返回全部键和值(都是节点)。 遍历整个字典, 用 dictGetKey 函数返回字典的键, 用 dictGetVal 函数返回字典的值。

(4)集合对象

集合命令的实现方法

命令 intset 编码的实现方法 hashtable 编码的实现方法
SADD 调用 intsetAdd 函数, 将全部新元素添加到整数集合里面。 调用 dictAdd , 以新元素为键, NULL 为值, 将键值对添加到字典里面。
SCARD 调用 intsetLen 函数, 返回整数集合所包含的元素数量, 这个数量就是集合对象所包含的元素数量。 调用 dictSize 函数, 返回字典所包含的键值对数量, 这个数量就是集合对象所包含的元素数量。
SISMEMBER 调用 intsetFind 函数, 在整数集合中查找给定的元素, 若是找到了说明元素存在于集合, 没找到则说明元素不存在于集合。 调用 dictFind 函数, 在字典的键中查找给定的元素, 若是找到了说明元素存在于集合, 没找到则说明元素不存在于集合。
SMEMBERS 遍历整个整数集合, 使用 intsetGet 函数返回集合元素。 遍历整个字典, 使用 dictGetKey 函数返回字典的键做为集合元素。
SRANDMEMBER 调用 intsetRandom 函数, 从整数集合中随机返回一个元素。 调用 dictGetRandomKey 函数, 从字典中随机返回一个字典键。
SPOP 调用 intsetRandom 函数, 从整数集合中随机取出一个元素, 在将这个随机元素返回给客户端以后, 调用 intsetRemove 函数, 将随机元素从整数集合中删除掉。 调用 dictGetRandomKey 函数, 从字典中随机取出一个字典键, 在将这个随机字典键的值返回给客户端以后, 调用 dictDelete 函数, 从字典中删除随机字典键所对应的键值对。
SREM 调用 intsetRemove 函数, 从整数集合中删除全部给定的元素。 调用 dictDelete 函数, 从字典中删除全部键为给定元素的键值对。

(4)集合对象

集合命令的实现方法

命令 intset 编码的实现方法 hashtable 编码的实现方法
SADD 调用 intsetAdd 函数, 将全部新元素添加到整数集合里面。 调用 dictAdd , 以新元素为键, NULL 为值, 将键值对添加到字典里面。
SCARD 调用 intsetLen 函数, 返回整数集合所包含的元素数量, 这个数量就是集合对象所包含的元素数量。 调用 dictSize 函数, 返回字典所包含的键值对数量, 这个数量就是集合对象所包含的元素数量。
SISMEMBER 调用 intsetFind 函数, 在整数集合中查找给定的元素, 若是找到了说明元素存在于集合, 没找到则说明元素不存在于集合。 调用 dictFind 函数, 在字典的键中查找给定的元素, 若是找到了说明元素存在于集合, 没找到则说明元素不存在于集合。
SMEMBERS 遍历整个整数集合, 使用 intsetGet 函数返回集合元素。 遍历整个字典, 使用 dictGetKey 函数返回字典的键做为集合元素。
SRANDMEMBER 调用 intsetRandom 函数, 从整数集合中随机返回一个元素。 调用 dictGetRandomKey 函数, 从字典中随机返回一个字典键。
SPOP 调用 intsetRandom 函数, 从整数集合中随机取出一个元素, 在将这个随机元素返回给客户端以后, 调用 intsetRemove 函数, 将随机元素从整数集合中删除掉。 调用 dictGetRandomKey 函数, 从字典中随机取出一个字典键, 在将这个随机字典键的值返回给客户端以后, 调用 dictDelete 函数, 从字典中删除随机字典键所对应的键值对。
SREM 调用 intsetRemove 函数, 从整数集合中删除全部给定的元素。 调用 dictDelete 函数, 从字典中删除全部键为给定元素的键值对。

 

(6)内存回收

对象的引用计数信息会随着对象的使用状态而不断变化:

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

(7)对象共享

Redis 中, 让多个键共享同一个值对象须要执行如下两个步骤:

  1. 将数据库键的值指针指向一个现有的值对象;
  2. 将被共享的值对象的引用计数增一。

目前来讲, Redis 会在初始化服务器时, 建立一万个字符串对象, 这些对象包含了从 0 到 9999 的全部整数值, 当服务器须要用到值为 0到 9999 的字符串对象时, 服务器就会使用这些共享对象, 而不是新建立对象。

为何 Redis 不共享包含字符串的对象?

当服务器考虑将一个共享对象设置为键的值对象时, 程序须要先检查给定的共享对象和键想建立的目标对象是否彻底相同, 只有在共享对象和目标对象彻底相同的状况下, 程序才会将共享对象用做键的值对象, 而一个共享对象保存的值越复杂, 验证共享对象和目标对象是否相同所需的复杂度就会越高, 消耗的 CPU 时间也会越多:

  • 若是共享对象是保存整数值的字符串对象, 那么验证操做的复杂度为 O(1) ;
  • 若是共享对象是保存字符串值的字符串对象, 那么验证操做的复杂度为 O(N) ;
  • 若是共享对象是包含了多个值(或者对象的)对象, 好比列表对象或者哈希对象, 那么验证操做的复杂度将会是 O(N^2) 。

所以, 尽管共享更复杂的对象能够节约更多的内存, 但受到 CPU 时间的限制, Redis 只对包含整数值的字符串对象进行共享。

(8)对象的空转时长

 键的空转时长还有另一项做用: 若是服务器打开了 maxmemory 选项, 而且服务器用于回收内存的算法为 volatile-lru 或者 allkeys-lru , 那么当服务器占用的内存数超过了 maxmemory 选项所设置的上限值时, 空转时长较高的那部分键会优先被服务器释放, 从而回收内存。

重点回顾

  • Redis 数据库中的每一个键值对的键和值都是一个对象。
  • Redis 共有字符串、列表、哈希、集合、有序集合五种类型的对象, 每种类型的对象至少都有两种或以上的编码方式, 不一样的编码能够在不一样的使用场景上优化对象的使用效率。
  • 服务器在执行某些命令以前, 会先检查给定键的类型可否执行指定的命令, 而检查一个键的类型就是检查键的值对象的类型。
  • Redis 的对象系统带有引用计数实现的内存回收机制, 当一个对象再也不被使用时, 该对象所占用的内存就会被自动释放。
  • Redis 会共享值为 0 到 9999 的字符串对象。
  • 对象会记录本身的最后一次被访问的时间, 这个时间能够用于计算对象的空转时间。
相关文章
相关标签/搜索