读书笔记:《Redis设计与实现》(第二部分)

Redis对象

对象的类型与编码

  • 每次当我们在Redis的数据库中新创建一个键值对时,我们至少会创建两个对象,一个对象用作键值对的键(键对象),另一个对象用作键值对的值(值对象)
  • 每个对象都是由一个redisObject结构表示
typedef struct redisObject {
    //类型
    unsigned type:4;
    //编码
    unsigned encoding:4;
    //指向底层实现数据结构的指针
    void *ptr;
    // ...
} robj;

类型

  • 字符串对象 REDIS_STRING
    • 编码可以是int、raw或者embstr
      • 如果一个字符串对象保存的是整数值,并且这个整数值可以用long表示,用字符串编码会使用int
      • 如果字符串对象保存的是字符串, 长度大于32字节,那么字符串对象使用简单动态字符串保存字符串值,编码记为raw
      • 如果字符串对象保存的是字符串,长度小于等于32字节,使用embstr编码
      • embstr与raw区别
        • embstr编码将创建字符串对象所需的内存分配次数从raw编码的两次降低为一次
        • 释放embstr编码的字符串对象只需要调用一次内存释放函数,而释放raw编码的字符串对象需要调用两次内存释放函数。
        • 因为embstr编码的字符串对象的所有数据都保存在一块连续的内存里面,所以这种编码的字符串对象比起raw编码的字符串对象能够更好地利用缓存带来的优势。
    • 编码之间的转换
      • int编码、embstr编码在一定条件下会转换为raw编码,如 APPEND方法等
  • 列表对象 REDIS_LIST
    • 编码可以为ziplist或者linkedlist
    • 使用ziplist需要满足如下条件:
      • 列表对象保存的所有字符串元素的长度都小于64字节
      • 列表对象保存的数量小于512个
    • 如果不满足使用ziplist的条件,则会转换为linkedlist(双端链表)
  • 哈希对象 REDIS_HASH
    • 编码可以为ziplist或者hashtable
      • 使用ziplist
        • 保存同一键值对的两个节点总是紧挨着, 保存键的节点在前, 保存值的节点在后
        • 先添加到哈希对象中的键值对会被放在压缩列表的表头方向,而后来添加到哈希对象中的键值对会被放在压缩列表的表尾方向。
      • HASHTABLE编码(字典作为底层实现)
        • 字典的每个键都是一个字符串对象,对象中保存了键值对的键;
        • 字典的每个值都是一个字符串对象,对象中保存了键值对的值。
    • 编码转换
      • 字典的每个值都是一个字符串对象,对象中保存了键值对的值。
        • 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节;
        • 哈希对象保存的键值对数量小于512个;
        • 注意: 以上两个条件可以修改设置
      • 不能满足这两个条件的哈希对象需要使用hashtable编码。
  • 集合对象 REDIS_SET
    • 编码可以为intset或者hashtable
      • intset
        • intset编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合里面
      • hashtable
        • hashtable编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,而字典的值则全部被设置为NULL。
    • 编码转换
      • 使用 intset编码条件
        • 集合对象保存的所有元素都是整数值
        • 集合对象保存的元素数量不超过512个
      • 不能满足intset条件的都为hashtable编码
  • 有序集合对象 REDIS_ZSET
    • 有序集合的编码可以是ziplist或者skiplist配合字典
      • ziplist
        • 每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员(member),而第二个元素则保存元素的分值(score)
        • 压缩列表内的集合元素按分值从小到大进行排序
      • skiplist
        • skiplist编码的有序集合对象使用zset结构作为底层实现
        • zset结构包含一个跳跃表和一个字典
        typedef struct zset {
            zskiplist *zsl;
            dict *dict;
        } zset;
        
        • 虽然使用字典和跳跃表同时存储数据,但是通过底层指针共享来完成,所以不会产生重复的数据,也不会浪费内存
        • 编码转换
          • 满足以下条件使用ziplist
            • 有序集合保存的元素数量小于128个
            • 有序集合保存的所有元素成员的长度都小于64字节
          • 如果不能满足以上条件,使用skiplist
    • 有序集合为什么使用跳跃表和字典来实现?
      • 单独使用一种结构性能都会降低
        • 使用字典: 查找优秀,但是范围操作都要进行排序
        • 使用跳跃表: 范围操作有点保存下来,但是根据成员去查找分值的能力降低

编码和底层实现

  • 使用 OBJECT ENCODING 查看数据库键的值对象的编码
类型编码对象
REDIS_STRINGREDIS_ENCDING_INT使用整数值实现的字符串对象
REDIS_STRINGREDIS_ENCDING_EMBSTR使用embstr编码的简单动态字符串对象实现的字符串对象
REDIS_STRINGREDIS_ENCDING_RAW使用简单动态字符串实现的字符串对象
REDIS_LISTREDIS_ENCODING_ZIPLIST使用压缩列表实现的列表对象
REDIS_LISTREDIS_ENCODING_LINKEDLIST使用双端链表实现的列表对象
REDIS_HASHREDIS_ENCODING_ZIPLIST使用压缩列表实现的哈希对象
REDIS_HASHREDIS_ENCODING_HT使用字典实现的哈希对象
REDIS_SETREDIS_ENCODING_INTLIST使用整数集合实现的集合对象
REDIS_SETREDIS_ENCODING_HT使用字典实现的集合对象
REDIS_ZSETREDIS_ENCODING_ZIPLIST使用压缩列表实现的有序集合对象
REDIS_ZSETREDIS_ENCODING_SKIPLIST使用跳跃表和字典实现的列表对象

类型检查与命令多态

  • 操作键的命令分为两种
    • 对任何键执行(DEL、EXPIRE、RENAME、TYPE、OBJECT等)
    • 对特定类型的键执行(SET、GET、APPEND、STRLEN等)
  • 类型检查
    • 在执行特定类型命令时,根据 redisObject 的 type属性检查是否可以执行
    • 多态命令
      • redis会根据值对象的类型来判断键是否能执行外,还会根据对象的编码来选择正确的命令实现代码来执行

内存回收

  • 引用计数技术实现内存回收机制
    • 在创建一个新对象时,引用计数的值会被初始化为1;
    • 当对象被一个新程序使用时,它的引用计数值会被增一;
    • 当对象不再被一个程序使用时,它的引用计数值会被减一;
    • 当对象的引用计数值变为0时,对象所占用的内存会被释放

对象共享

  • 步骤
    • 将数据库键的值指针指向一个现有的值对象;
    • 将被共享的值对象的引用计数增一
  • 具体做法
    • Redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值,当服务器需要用到值为0到9999的字符串对象时,服务器就会使用这些共享对象,而不是新创建对象。
  • 为什么不共享字符串对象
    • 创建共享对象设置为键的值对象时,需要先检查键想创建的目标对象是否完全相同,只有完全相同才会创建
      • 如果共享对象是保存整数值的字符串对象,那么验证操作的复杂度为O(1);
      • 如果共享对象是保存字符串值的字符串对象,那么验证操作的复杂度为O(N);
      • 如果共享对象是包含了多个值(或者对象的)对象,比如列表对象或者哈希对象,那么验证操作的复杂度将会是O(N 2)
    • 所以, 只共享数值类型数据

对象的空转时长-lru

  • 除了对象的type、encoding、ptr、refcount四个属性外, redisObject结构还包含lru属性,用于记录对象最后一次被命令程序访问的时间
  • OBJECT IDLETIME 命令可以打印出给定键的空转时长
  • lru作用
    • 计算空转时长
    • 用于服务器释放内存
      • volatile-lru
      • allkeys-lru
      • 空转时长较高的会优先被服务器释放
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值