首页 > 数据库 >一文彻底搞懂Redis底层数据结构

一文彻底搞懂Redis底层数据结构

时间:2024-03-25 11:29:23浏览次数:16  
标签:存储 Redis 列表 链表 搞懂 数据结构 节点

文章目录

1. 数据结构与数据类型的关系

Redis是一个基于内存的数据存储系统,它支持多种数据结构和数据类型,每种数据结构和数据类型都有其特定的用途和适用场景。下面是Redis支持的主要数据结构及其对应的数据类型:

  • 字符串(String):字符串是Redis中最简单的数据结构,可以存储文本、数字等任意类型的数据。在Redis中,字符串类型被用于缓存、计数器等场景。
  • 列表(List):列表是一个有序的字符串集合,可以包含重复的元素。在Redis中,列表适用于存储按顺序排列的数据,比如消息队列、任务列表等。
  • 哈希表(Hash):哈希表是一个键值对集合,每个键值对都存储在一个哈希表中。在Redis中,哈希表适用于存储对象的属性和值之间的映射关系,比如存储用户信息、配置信息等。
  • 集合(Set):集合是一个无序的字符串集合,不允许重复元素。在Redis中,集合适用于存储唯一的、不重复的数据,比如用户标签、文章标签等。
  • 有序集合(Sorted Set):有序集合是一个有序的字符串集合,每个字符串都关联一个分数(score)。在Redis中,有序集合适用于存储按分数排序的数据,比如排行榜、计分系统等。
    在这里插入图片描述

2. 底层数据结构详解

2.1 SDS: 简单动态字符串

SDS(Simple Dynamic Strings)是Redis中用于表示字符串的数据结构之一,它具有以下特点:

  • 灵活性: SDS 可以存储字符串,也可以存储二进制数据,包括空字符,因此在处理二进制数据时更为灵活,不受空字符的限制。
  • 缓存长度信息: SDS 在头部保存了字符串的长度信息,因此可以在 O(1) 的时间复杂度内获取字符串的长度,而不需要遍历整个字符串,提高了获取长度的效率。
  • 动态扩容: SDS 可以根据实际存储的数据动态扩容,当字符串长度变长时,SDS 会自动进行内存的扩展,而不需要手动管理内存,提高了内存管理的便利性。

SDS 的设计使得它在处理字符串时更为灵活和高效,因此在 Redis 中被广泛应用于存储和处理字符串数据。

2.2 双端链表

Redis中的 List 就是⽤双端链表实现的,双端链表(Doubly Linked List)是一种链表数据结构,每个节点除了包含指向下一个节点的指针外,还包含指向上一个节点的指针,双端链表的链表节点可以保存不同类型的值,⽀持在两端进⾏元素的快速插⼊和删除,并且链表结构提供了表头指针和表尾指针,获取链表的表头节点和表尾节点的时间复杂度只需O(1);
获取链表数量的时间复杂度也只需O(1)。

双端链表的节点结构通常包含以下两个指针:

  • 指向下一个节点的指针(Next Pointer):指向链表中的下一个节点。
  • 指向上一个节点的指针(Previous Pointer):指向链表中的上一个节点。

双端链表的优点:

  • 可以在头部和尾部高效地进行插入和删除操作,时间复杂度为 O(1)。
  • 可以支持双向遍历,能够从头部和尾部同时进行遍历操作。

双端链表的缺陷:

  • 链表每个节点之间的内存都是不连续的,意味着⽆法很好利⽤ CPU 缓存
  • 保存⼀个链表节点的值都需要⼀个链表节点结构头的分配,内存开销较⼤。

双端链表常被用于需要频繁进行插入和删除操作,并且需要支持双向遍历的场景,比如实现队列、栈、LRU缓存等数据结构和算法。

2.3 压缩列表

压缩列表(ZipList)是一种用于在 Redis 中紧凑存储列表和哈希表数据的数据结构。它是由一系列特定编码格式的连续内存块组成的顺序型数据结构,每个内存块称为一个节点(entry)。每个节点可以存储一个字节数组或整数值,并且可以根据实际存储的数据动态地调整节点的长度。

压缩列表的主要特点包括:

  • 紧凑存储:压缩列表采用紧凑的内存布局,节点之间紧密相连,节省了内存空间。
  • 可变长度:每个节点的长度可以根据实际存储的数据动态地调整,避免了不必要的内存浪费。
  • 支持多种数据类型:压缩列表可以存储不同类型的数据,包括字符串和整数值。
  • 快速随机访问:压缩列表支持通过索引快速定位到指定位置的节点,因此可以在 O(1) 的时间复杂度内实现随机访问。
  • 适应于小规模数据:压缩列表适用于存储小规模的数据集,当数据量较大时,会转换为其他更适合大规模数据的数据结构,比如双端链表或跳表。

压缩列表在 Redis 中被广泛应用于列表和哈希表数据类型的实现中,能够高效地存储和操作数据,提升了 Redis 的性能和内存利用率。

2.4 哈希表

哈希表(Hash Table)是一种用于存储键值对(key-value pairs)的数据结构,在 Redis 中被广泛应用于实现各种数据类型,如哈希表(Hashes)和集合(Sets)等。

在哈希表中,每个键都经过哈希函数处理后映射到一个唯一的索引位置,然后将对应的值存储在该位置上。这样,通过键可以快速定位到对应的值,实现了高效的查找操作。

哈希表的主要特点包括:

  • 快速查找:通过键进行哈希计算,可以快速定位到对应的值,具有 O(1) 的平均时间复杂度。
  • 灵活性:哈希表可以存储各种类型的键值对,不限于字符串和整数。
  • 动态扩容:在数据量增加时,哈希表可以动态地扩容以适应新的键值对的存储需求。
  • 碰撞处理:由于哈希函数可能会将不同的键映射到同一个索引位置上,因此需要使用碰撞处理技术来解决冲突,常见的方法包括链地址法和开放定址法等。

在 Redis 中,哈希表被用于实现哈希表数据类型(Hashes),它可以存储多个键值对,并且支持对单个键值对的增、删、改、查等操作。此外,哈希表还被用于实现集合数据类型(Sets)中的有序集合(Sorted Sets),用于存储成员和对应的分值之间的关系。

2.5 整数集合

整数集合(Integers Set)是 Redis 中的一种特殊数据结构,用于高效地存储整数值的集合。它是为了在存储一组整数时,提供更加紧凑和高效的存储方式而设计的。

整数集合的主要特点包括:

  • 紧凑存储:整数集合使用紧凑的数据结构存储整数值,不像普通的数组那样每个元素都需要占用固定的内存空间,而是根据整数的大小动态调整所需的存储空间,节省了内存的使用。
  • 快速查找:由于整数集合中的元素是有序的,并且使用了特定的算法进行存储和查找,因此可以在 O(logN) 的时间复杂度内实现查找操作,其中 N 表示整数集合中的元素个数。
  • 支持多种整数类型:整数集合可以存储多种整数类型,包括 int16、int32、int64 等,根据存储的整数值的大小自动选择合适的数据类型。
  • 动态调整:整数集合在存储整数值的过程中,会根据需要动态调整存储空间,避免了固定大小数组可能带来的空间浪费或溢出问题。

整数集合在 Redis 中被广泛应用于存储集合数据类型(Sets)中的整数成员,例如用于存储用户 ID、商品 ID 等。由于其高效的存储方式和快速的查找性能,使得整数集合成为了处理整数集合类数据的首选数据结构之一。

2.6 跳表

跳跃表是⼀种在链表基础上改进过来的,实现了⼀种「多层」的有序链表,当数据量很⼤时,跳表的查找复杂度就
是O(logN)。在 Redis 中,有序集合(Sorted Set)就是通过跳表来实现的,因为跳表具有快速的查找和插入操作,非常适合用于实现有序集合类数据结构。

跳表的查找过程?

1.查找⼀个跳表节点的过程时,跳表会从头节点的最⾼层开始,逐⼀遍历每⼀层。在遍历某⼀层的跳表节点时,会⽤
跳表节点中的 SDS 类型的元素和元素的权重来进⾏判断:

2.如果当前节点的权重⼩于要查找的权重时,跳表就会访问该层上的下⼀个节点。

3.如果当前节点的权重等于要查找的权重时,并且当前节点的 SDS 类型数据⼩于要查找的数据时,跳表就会访
问该层上的下⼀个节点。

4.如果上⾯两个条件都不满⾜,或者下⼀个节点为空时,跳表就会使⽤⽬前遍历到的节点的 level 数组⾥的下⼀层指
针,然后沿着下⼀层指针继续查找。

跳表的主要特点包括:

  • 多级索引:跳表通过维护多级索引,使得在查找时可以跳过部分元素,从而减少查找的时间复杂度。每一级索引都是一个- 有序链表,最底层的链表包含所有元素,而上层的链表包含更少的元素,相邻两级索引的元素数量相差较大。
  • 插入和删除高效:由于跳表中的每一级索引都是有序的链表,因此在插入和删除元素时,只需要调整相邻节点的指针即可,不需要像平衡树那样进行复杂的平衡操作,因此插入和删除操作的时间复杂度为 O(logN),其中 N 表示跳表中的元素数量。
  • 支持快速查找:由于跳表维护了多级索引,因此在查找时可以根据索引快速定位到目标元素所在的范围,然后再在范围内使用顺序查找进行查找,这样可以将查找的时间复杂度降低到 O(logN)。

跳表相对于平衡树来说实现更加简单,并且在实际应用中具有较好的性能表现。

2.7 quicklist

Quicklist(快速列表)是 Redis 中用于存储列表类型数据的一种高效数据结构。它是双向链表 + 压缩列表的混合体,quicklist 就是⼀个链表,⽽链表中的每个元素⼜是⼀个压缩列表。 quicklist 解决办法,通过控制每个链表节点中的压缩列表的⼤⼩或者元素个数,来规避连锁更新的问题。因为压缩列表元素越少或越⼩,连锁更新带来的影响就越⼩,从⽽提供了更好的访问性能。可以在列表的两端进行快速插入和删除操作,并且在内部使用压缩列表来节省内存空间。

快速列表的主要特点包括:

  • 双向链表特性:快速列表包含多个节点,每个节点都是一个双向链表。这使得在列表的两端进行插入和删除操作时具有较高的效率,时间复杂度为 O(1)。
  • 压缩列表优势:每个节点内部使用压缩列表来存储列表元素。压缩列表是一种紧凑的、可变长度的连续内存块,可以高效地存储多个元素,并且在元素数量较小时节省内存空间。
  • 节点间指针:快速列表中的节点之间通过指针进行连接,使得可以在节点之间进行快速的遍历和操作。
  • 灵活性:快速列表可以根据实际存储的数据动态调整节点的数量和大小,从而实现空间利用的最大化和性能的最优化。

由于快速列表同时具备双向链表和压缩列表的优势,因此在 Redis 中被广泛应用于存储列表类型数据,如列表、队列等,以满足不同场景下的需求。

2.8 listpack

Listpack 是 Redis 中用于存储列表类型数据的一种紧凑、高效的数据结构。它是一种类似于压缩列表的数据结构,但相比于压缩列表,Listpack 在一些方面进行了优化和改进。listpack 没有压缩列表中记录前⼀个节点⻓度的字段,listpack 只记录当前节点的⻓度,当我们向 listpack 加⼊⼀个新元素的时候,不会影响其他节点的⻓度字段的变化,从⽽避免了压缩列表的连锁更新问题。

Listpack 的主要特点包括:

  • 紧凑性:Listpack 在内存中存储数据时非常紧凑,它只记录当前节点的长度,而不记录前一个节点的长度。这种设计避免了像压缩列表那样可能引发的连锁更新问题,提高了内存使用的效率。
  • 高效性:Listpack 在插入和删除操作时具有较高的性能,因为它不需要更新前一个节点的长度字段。这使得对于列表中的某个节点进行操作时,不会影响到其他节点的数据结构,减少了操作的开销和复杂度。
  • 灵活性:Listpack 可以根据实际存储的数据动态调整节点的大小和数量,从而在不同场景下实现空间的最大化利用和性能的最优化。

总的来说,Listpack 是 Redis 中用于存储列表类型数据的一种高效、灵活的数据结构,它在内存使用效率和操作性能上具有较好的表现,适用于存储大量的列表数据,并且在 Redis 内部的实现中得到了广泛应用。

标签:存储,Redis,列表,链表,搞懂,数据结构,节点
From: https://blog.csdn.net/weixin_44772566/article/details/137008421

相关文章

  • redis面试题
    redis面试题一、基础知识面试1.说说你对Redis的理解Redis是一个基于Key-Value存储结构的开源内存数据库,也是一种NoSQL数据库。它支持多种数据类型,包括String、Map、Set、ZSet和List,以满足不同应用场景的需求。Redis以内存存储和优化的数据结构为基础,提供了快速的读写性能和高......
  • 微软的Redis替代项目——Garnet
    微软研究院最近开源了一个新的C#项目,叫Garnet,它实现了Redis协议,基本可以看做redis的替代品了。文档地址:https://microsoft.github.io/garnet/简单的看了下,它居然是以nuget包发布的,通过它我们可以直接快速实现一个自己的redisserver。usingGarnet;try{usingvarserve......
  • 数据结构练习-C语言
    1.假设有一个带头结点的单链表L,每个结点值由单个数字、小写字母和大写字母构成。设计一个算法将其拆分成3个带头结点的单链表L1、L2和L3,L1包含L中的所有数字结点,L2包含L中的所有小写字母结点,L3包含L中的所有大写字母结点。该算法如何设计?首先创建L1、L2、L3的头结点,......
  • Redis
    RedisRedis常规八股(Rediscommoninterviewquestionsandconcepts):Redis是什么?(WhatisRedis?):Redis是一个开源的内存数据存储系统,可以用作数据库、缓存和消息中间件。Redis的特点是什么?(WhatarethefeaturesofRedis?):Redis具有高性能、支持多种数据结构、提供持久化选项......
  • redis实际应用场景及并发问题的解决
    业务场景接下来要模拟的业务场景:每当被普通攻击的时候,有千分之三的概率掉落金币,每回合最多爆出两个金币。1.每个回合只有15秒。2.每次普通攻击的时间间隔是0.5s3.这个服务是一个集群(这个要求暂时不实现)编写接口,实现上述需求。核心问题可以想到要解决的主要问题是,1.如何......
  • 一篇文章搞懂并设计循环队列
    目录1.为什么使用循环队列2.循环队列组成为什么要只使用size-1个空间存储?3.循环队列的元素进出3.1队尾加入元素3.2队头删除元素3.3取出队头元素3.4取出队尾元素1.为什么使用循环队列“假溢出”——》出队列会空出存储空间,无法再次利用如图:索引为0和1的空......
  • 【数据结构】快速排序(用递归)
    大家好,我是苏貝,本篇博客带大家了解快速排序,如果你觉得我写的还不错的话,可以给我一个赞......
  • 【数据结构】希尔排序
    大家好,我是苏貝,本篇博客带大家了解希尔排序,如果你觉得我写的还不错的话,可以给我一个赞......
  • 【数据结构】归并排序(用递归)
    大家好,我是苏貝,本篇博客带大家了解归并排序,如果你觉得我写的还不错的话,可以给我一个赞......
  • [数据结构]二叉树的建立与遍历(递归)
    一、二叉树的遍历与建立首先我们拥有如下二叉树:要了解二叉树遍历,我们得先了解二叉树的三种遍历方式:前序遍历,中序遍历,后序遍历1.前序遍历前序遍历:根,左子树,右子树遍历的结果就是:1248NN9NN510NN11NN36NN7NN2.中序遍历中序遍历:左子树根......