热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析Redis的数据结构与对象系统

Redis是一个高性能的开源键值存储系统,支持多种数据结构。本文将详细介绍Redis中的六种底层数据结构及其在对象系统中的应用,包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象。通过12张图解,帮助读者全面理解Redis的数据结构和对象系统。

Redis 是一个高性能的开源键值存储系统,支持多种数据结构。本文将详细介绍 Redis 中的六种底层数据结构及其在对象系统中的应用,包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象。通过12张图解,帮助读者全面理解 Redis 的数据结构和对象系统。

本文内容如下:

  • 首先介绍六种基础数据结构:动态字符串、链表、字典、跳跃表、整数集合和压缩列表。
  • 其次介绍 Redis 的对象系统中的字符串对象(String)、列表对象(List)、哈希对象(Hash)、集合对象(Set)和有序集合对象(ZSet)。
  • 最后介绍 Redis 的键空间和过期键(expire)实现。

数据结构

简单动态字符串

Redis 使用动态字符串 SDS 来表示字符串值。下图展示了一个值为 Redis 的 SDS 结构:

  • len: 表示字符串的实际长度(不包含 NULL 结束符)。
  • alloc: 表示字符串的最大容量(不包含最后一个字节)。
  • flags: 占用一个字节,其中最低3位表示 header 的类型。
  • buf: 字符数组。

SDS 结构可以减少修改字符串时的内存重分配次数,主要依赖于内存预分配和惰性空间释放机制。当 SDS 需要扩展时,Redis 会分配额外的未使用空间:

  • 如果修改后的 SDS 长度小于 1MB,预分配与 len 相同大小的未使用空间。
  • 如果修改后的 SDS 长度大于 1MB,预分配 1MB 的未使用空间。

当 SDS 缩短其保存的字符串长度时,并不会立即释放多出来的字节,而是保留以供后续使用。

链表

链表在 Redis 中应用广泛,例如列表对象的底层实现之一就是链表。此外,发布和订阅、慢查询、监视器等功能也使用了链表。Redis 的链表是双向链表,如下图所示:

链表结构的 dup、free 和 match 成员属性用于实现多态链表所需的类型特定函数:

  • dup 函数用于复制链表节点所保存的值,实现深度拷贝。
  • free 函数用于释放链表节点所保存的值。
  • match 函数用于对比链表节点所保存的值和另一个输入值是否相等。

字典

字典广泛用于实现 Redis 的各种功能,包括键空间和哈希对象。Redis 使用 MurmurHash2 算法计算键的哈希值,并使用链地址法解决键冲突。被分配到同一个索引的多个键值对会连接成一个单向链表,如下图所示:

跳跃表

Redis 使用跳跃表作为有序集合对象的底层实现之一。跳跃表以有序的方式在层次化的链表中保存元素,效率与平衡树相当,但实现更为简单直观。查找、删除、添加等操作都可以在对数期望时间内完成。如下图所示:

跳表的节点 zskiplistNode 包含元素值 ele 和分值 score,节点按 score 值有序排列。每个节点的 level 数组大小不同,level 数组中的值是指向下一个节点的指针和跨度值(span)。越高层的 level 数组值的跨度值越大,底层的 level 数组值的跨度值越小。level 数组类似于不同刻度的尺子,度量长度时先用大刻度估计范围,再用小刻度进行精确逼近。

整数集合

整数集合 intset 是集合对象的底层实现之一,当集合只包含整数值元素且元素数量不多时,Redis 使用整数集合作为集合对象的底层实现。整数集合的 encoding 表示其类型,可以是 int16_t、int32_t 或 int64_t。每个元素都是 contents 数组的一个数组项,按值的大小从小到大有序排列,数组中不包含重复项。length 属性表示整数集合包含的元素数量,如下图所示:

压缩列表

压缩列表 ziplist 是列表对象和哈希对象的底层实现之一。当满足一定条件时,列表对象和哈希对象会以压缩列表为底层实现。压缩列表是 Redis 为了节省内存而开发的顺序型数据结构,由一系列特殊编码的连续内存块组成。其属性值包括:

  • zlbytes: 记录整个压缩数组的内存字节数。
  • zltail: 记录压缩列表表尾节点距离压缩列表起始地址的字节数。
  • zllen: 包含的节点数,当属性值小于 INT16_MAX 时,该值就是节点总数,否则需要遍历整个列表才能确定总数。
  • zlend: 特殊值,用于标记压缩列表的末端。

每个节点 entry 由三部分组成:

  • previous_entry_length: 前一个节点的长度,用于计算前一个节点的起始地址。
  • encoding: 节点保存数据的类型和长度。
  • content: 节点值,可以是一个字节数组或整数。

对象系统

Redis 并没有直接使用上述六种底层数据结构实现键值数据库,而是基于这些数据结构创建了一个对象系统,包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象。每个对象都使用至少一种底层数据结构,根据不同的使用场景和内容大小选择合适的数据结构,以优化对象在不同场景下的使用效率和内存占用。

Redis 的 redisObject 结构定义如下:

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS; 
    int refcount;
    void *ptr;
} robj;

其中 type 表示对象类型,包括 REDIS_STRING、REDIS_LIST、REDIS_HASH、REDIS_SET 和 REDIS_ZSET。encoding 表示对象使用的数据结构。

字符串对象

字符串对象的实现如下图所示。如果字符串对象保存的字符串长度大于 32 字节,使用 SDS 保存,编码设置为 raw。如果字符串长度小于 32 字节,使用 embstr 编码方式保存。embstr 编码通过一次内存分配创建两个结构,节省内存并提高缓存利用率,但不可修改。当 embstr 编码的字符串对象进行 append 操作时,Redis 会将其转换为 raw 编码。

列表对象

列表对象的编码可以是 ziplist 或 linkedlist。当列表对象满足以下条件时,使用 ziplist 编码:

  • 所有字符串元素的长度都小于 64 字节。
  • 元素数量小于 512 个。

否则使用 linkedlist 编码。

哈希对象

哈希对象的编码可以是 ziplist 或 dict。当哈希对象满足以下条件时,使用 ziplist 编码:

  • 所有键值对的键和值的字符串长度都小于 64 字节。
  • 键值对数量小于 512 个。

否则使用 dict 编码。

集合对象

集合对象的编码可以是 intset 或 dict。当集合对象满足以下条件时,使用 intset 编码:

  • 所有元素都是整数值。
  • 元素数量不超过 512 个。

否则使用 dict 编码。

有序集合对象

有序集合的编码可以是 ziplist 或 skiplist。当有序集合对象满足以下条件时,使用 ziplist 编码:

  • 元素数量少于 128 个。
  • 所有元素的长度都小于 64 字节。

否则使用 skiplist 编码。

数据库键空间

Redis 服务器有多个数据库,每个数据库有自己的键值空间。每个数据库使用 dict 保存所有键值对。键空间的键是字符串对象,值对象可以是字符串对象、列表对象、哈希表对象、集合对象或有序集合对象中的一种。Redis 也使用 dict 结构保存键的过期时间,键是键空间中的键值,值是过期时间。通过过期字典,Redis 可以直接判断一个键是否过期。


推荐阅读
  • 深入解析Redis内存对象模型
    本文详细介绍了Redis内存对象模型的关键知识点,包括内存统计、内存分配、数据存储细节及优化策略。通过实际案例和专业分析,帮助读者全面理解Redis内存管理机制。 ... [详细]
  • 深入理解Redis的数据结构与对象系统
    本文详细探讨了Redis中的数据结构和对象系统的实现,包括字符串、列表、集合、哈希表和有序集合等五种核心对象类型,以及它们所使用的底层数据结构。通过分析源码和相关文献,帮助读者更好地理解Redis的设计原理。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 本次考试于2016年10月25日上午7:50至11:15举行,主要涉及数学专题,特别是斐波那契数列的性质及其在编程中的应用。本文将详细解析考试中的题目,并提供解题思路和代码实现。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • Python自动化处理:从Word文档提取内容并生成带水印的PDF
    本文介绍如何利用Python实现从特定网站下载Word文档,去除水印并添加自定义水印,最终将文档转换为PDF格式。该方法适用于批量处理和自动化需求。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • PHP 5.5.0rc1 发布:深入解析 Zend OPcache
    2013年5月9日,PHP官方发布了PHP 5.5.0rc1和PHP 5.4.15正式版,这两个版本均支持64位环境。本文将详细介绍Zend OPcache的功能及其在Windows环境下的配置与测试。 ... [详细]
  • MySQL索引详解与优化
    本文深入探讨了MySQL中的索引机制,包括索引的基本概念、优势与劣势、分类及其实现原理,并详细介绍了索引的使用场景和优化技巧。通过具体示例,帮助读者更好地理解和应用索引以提升数据库性能。 ... [详细]
  • 本题通过将每个矩形视为一个节点,根据其相对位置构建拓扑图,并利用深度优先搜索(DFS)或状态压缩动态规划(DP)求解最小涂色次数。本文详细解析了该问题的建模思路与算法实现。 ... [详细]
  • 最近团队在部署DLP,作为一个技术人员对于黑盒看不到的地方还是充满了好奇心。多次咨询乙方人员DLP的算法原理是什么,他们都以商业秘密为由避而不谈,不得已只能自己查资料学习,于是有了下面的浅见。身为甲方,虽然不需要开发DLP产品,但是也有必要弄明白DLP基本的原理。俗话说工欲善其事必先利其器,只有在懂这个工具的原理之后才能更加灵活地使用这个工具,即使出现意外情况也能快速排错,越接近底层,越接近真相。根据DLP的实际用途,本文将DLP检测分为2部分,泄露关键字检测和近似重复文档检测。 ... [详细]
  • 本文深入探讨了 Redis 的两种持久化方式——RDB 快照和 AOF 日志。详细介绍了它们的工作原理、配置方法以及各自的优缺点,帮助读者根据具体需求选择合适的持久化方案。 ... [详细]
  • 本文详细介绍了在企业级项目中如何优化 Webpack 配置,特别是在 React 移动端项目中的最佳实践。涵盖资源压缩、代码分割、构建范围缩小、缓存机制以及性能优化等多个方面。 ... [详细]
  • 本文详细介绍如何利用已搭建的LAMP(Linux、Apache、MySQL、PHP)环境,快速创建一个基于WordPress的内容管理系统(CMS)。WordPress是一款流行的开源博客平台,适用于个人或小型团队使用。 ... [详细]
author-avatar
佩弦_秦子轩_188
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有