第8章 对象

Huan Lee Lv5

Redis并没有直接使用SDS, 链表, 字典等数据结构来实现键值对数据库, 而是基于这些数据结构构建了一个对象系统, 包含字符串对象, 列表对象, 哈希对象, 集合对象和有序集合对象五类.

8.1 对象的类型与编码

每次在Redis中新建一个键值对时, 会至少创建两个对象, 分别用作键(总是字符串对象)和值

Untitled

Untitled

8.2 字符串对象

字符串对象的编码可以是int, raw和embstr

  • 如果一个字符串保存的是可以用long表示的整数值, 那么字符串对象会将整数值保存到ptr里面(将void* 转换成long)

  • 如果字符串长度大于32字节, 使用SDS(raw)

  • 如果小于等于32字节, 使用embstr编码. redisObject和sdshdr保存在一块连续内存中.

    • 申请和释放内存时都只用调用一次相关函数(raw需要两次, 分别针对redisObject和sdshdr)

    • 更好地利用缓存带来的优势

    • embstr没配备修改程序, 因此该类型是只读的, 若要做出修改, 会被转换为raw类型

    Untitled

  • 浮点数会被转换成字符串值进行保存.

  • 字符串对象是Redis五种类型的对象中唯一一种会被其他四种类型对象嵌套的对象.

Untitled

8.3 列表对象

列表对象可以是压缩列表(ziplist)或者双端链表(linkedlist).

当列表对象可以同时满足以下两个条件时,列表对象使用ziplist编码:

  • 列表对象保存的所有字符串元素的长度都小于64字节
  • 列表对象保存的元素数量小于512个
  • 条件的具体上限值可以在配置文件中修改

Untitled

8.4 哈希对象

哈希对象的编码可以是ziplist或者hashtable

  • 以ziplist作为底层实现的哈希对象, 会依次将键和值加入列表末尾
  • 以hashtable编码的哈希对象底层通过字典进行实现, 每个键值对的键和值都是字符串对象

当哈希对象可以同时满足以下两个条件时,哈希对象使用ziplist编码:

  • 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节;
  • 哈希对象保存的键值对数量小于512个;

Untitled

8.5 集合对象

集合对象的编码可以是intset或者hashtable

  • 使用hashtable编码的集合对象使用字典作为底层实现, 字典的每个键都是一个字符串对象, 表示一个集合元素, 而字典的值呗设置为NULL.

当集合对象可以同时满足以下两个条件时,对象使用intset编码:

  • 集合对象保存的所有元素都是整数值
  • 集合对象保存的元素数量不超过512个

Untitled

Untitled

8.6 有序集合对象

有序集合的编码可以是ziplist或者skiplist

  • 使用ziplist作为底层实现时, 每个集合元素使用两个挨在一起的压缩列表来保存, 分别记录元素的成员和分值(score). 元素按分值从小到大排列.
  • skiplist编码的有序集合使用zset作为底层实现, 一个zset同时包含一个字典和一个跳跃表. 跳跃表按分值从小到大保存了所有集合元素, 字典则为有序集合创建了一个从成员到分值的映射, 键为成员, 值为分值. (字典和跳跃表会共享元素的成员和分值, 因此不会造成数据重复)

当有序集合对象可以同时满足以下两个条件时,对象使用ziplist编码:

  • 有序集合保存的元素数量小于128个
  • 有序集合保存的所有元素成员的长度都小于64字节

Untitled

8.7 类型检查与命令多态

  • 对任意类型的键均可执行的命令: DEL, EXPIRE, RENAME, TYPE, OBJECT等

  • 只能对特定类型的键执行:

    • 字符串键: SET, GET, APPEND, STRLEN等

    • 哈希键: HDEL, HSET, HGET, HLEN等

    • 列表键: RPUSH, LPOP, LINSERT, LLEN等

    • 集合键: SADD, SPOP, SINTER, SCARD等

    • 有序集合键: ZADD, ZCARD, ZRANK, ZSCORE等

  • 在执行一个类型特定的命令前, Redis会进行检测

多态命令的实现

Redis还会根据对象的编码方式, 选择对应的命令实现代码. 无论具体是哪种底层实现, 都会执行命令, 因此称这样的命令是多态的.

8.8 内存回收

Redis为对象系统构建了一个引用计数的内存回收机制.

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

  • 在创建一个新对象时,refcount = 1
  • 当对象被一个新程序使用时,refcount+1
  • 当对象不再被一个程序使用时,refcount-1
  • 当refcount == 0时,对象所占用的内存会被释放

8.9 对象共享

对象的引用计数还带有对象共享的作用.

Redis会在初始化服务器时, 创建一万个字符串对象, 涵盖从0-9999的所有整数值, 用作共享对象.

8.10 对象空转时长

redisObject还维护一个lru属性, 表示对象最后一次被命令程序访问的时间.

  • 可以通过 OBJECT IDLETIME 打印出来
  • 如果服务器打开了maxmemory选项, 并且服务器的回收内存算法为volatile-lru或者allkeys-lru, 那么当占用内存超过上限时, 空转时长较高的那部分键会优先被释放.
  • Title: 第8章 对象
  • Author: Huan Lee
  • Created at : 2023-08-25 19:48:50
  • Updated at : 2024-02-26 04:53:15
  • Link: https://www.mirthfullee.com/2023/08/25/notion-第8章 对象-d6fbaebc/
  • License: This work is licensed under CC BY-NC-SA 4.0.