自学内容网 自学内容网

Redis 五种数据类型及底层数据结构详解

目录

  1. Redis 数据类型概览
  2. String 类型及底层实现
    • 2.1 简单动态字符串(SDS)
  3. Hash 类型及底层实现
    • 3.1 哈希表(Hash Table)
    • 3.2 压缩列表(Ziplist)
  4. List 类型及底层实现
    • 4.1 双向链表(LinkedList)
    • 4.2 压缩列表(Ziplist)
  5. Set 类型及底层实现
    • 5.1 哈希表
    • 5.2 整数集合(Intset)
  6. Sorted Set 类型及底层实现
    • 6.1 跳表(SkipList)
    • 6.2 压缩列表(Ziplist)
  7. 总结

Redis 数据类型概览

Redis 提供的五种主要数据类型,每一种都有其特定的用途:

  • String:最基础的键值对形式,可以存储字符串、整数、浮点数。
  • Hash:适合存储对象,可以用字段-值的形式存储数据。
  • List:链表结构,支持从两端操作,非常适合实现队列或栈。
  • Set:无序的唯一集合,适合存储不重复的数据。
  • Sorted Set:有序的集合,每个元素都会关联一个分数,支持按分数排序。

这些类型的底层实现并不完全相同,Redis 会根据存储的元素数量和数据结构自动选择最合适的存储方式。

String 类型及底层实现

String 是 Redis 中最基础的数据类型,一个键对应一个值。String 类型的值可以是普通字符串、数字、二进制数据等。其主要应用场景包括:

  • 缓存字符串数据。
  • 作为计数器存储整数或浮点数。
  • 存储用户信息等简单数据。

2.1 简单动态字符串(SDS)

Redis 的 String 类型底层使用的是 SDS(Simple Dynamic String) 来实现。SDS 是 Redis 为了替代 C 语言中的 char 类型字符串而设计的一种动态字符串结构,具有更高的灵活性和安全性。

SDS 的主要特点:

  • 动态扩展:当字符串长度增加时,SDS 可以自动扩展其容量,避免频繁的内存分配。
  • 预分配空间:SDS 为了减少内存重分配的次数,会预留一部分额外空间,尤其是在增长字符串时。
  • 惰性空间释放:当缩短 SDS 字符串时,并不会立即释放多余的内存,而是保留这些空间,等待未来使用。
  • 二进制安全:SDS 可以存储任意二进制数据,而不仅仅是文本数据。

SDS 的结构如下:

struct sdshdr {
    int len;         // 已使用的长度
    int free;        // 剩余的可用空间
    char buf[];      // 实际保存字符串的地方
};

这种结构设计使得 SDS 在处理字符串时具备高效性和灵活性。

Hash 类型及底层实现

Hash 类型在 Redis 中用于存储对象数据,类似于传统编程语言中的字典或 Map 结构。每个键对应一个字段和字段值的集合,非常适合存储用户信息等对象数据。

3.1 哈希表(Hash Table)

当 Hash 类型的数据量较大时,Redis 使用经典的 哈希表(Hash Table) 来存储。哈希表通过计算键的哈希值将字段-值对映射到不同的桶中,从而实现快速的 O(1) 复杂度的查找、插入和删除操作。

Redis 使用的哈希表结构为 dict,其主要结构如下:

typedef struct dict {
    dictEntry **table;   // 哈希桶数组
    unsigned long size;  // 哈希表的大小
    unsigned long sizemask;  // 用于计算索引的掩码
    unsigned long used;  // 已使用的哈希桶数量
} dict;

Redis 为了防止哈希冲突,使用了链地址法来解决,当多个键映射到同一个桶时,这些键值对会以链表的形式存储在该桶中。

3.2 压缩列表(Ziplist)

当 Hash 中的字段数量较少且每个字段和字段值都很小的时候,Redis 会使用更节省内存的 压缩列表(Ziplist) 结构来存储数据。

压缩列表是一种紧凑的双向链表,所有元素紧密排列在一起,不会为每个元素单独分配内存。这种结构极大地减少了内存的使用,但在数据量较大时,性能会有所下降。

List 类型及底层实现

List 是 Redis 中的链表结构,支持从两端进行高效的插入和删除操作,适合用于实现队列、栈等数据结构。List 常用于社交网络的消息流、任务队列等场景。

4.1 双向链表(LinkedList)

当 List 中存储的元素较多时,Redis 使用 双向链表(LinkedList) 结构来实现。双向链表允许我们在 O(1) 时间内从头部和尾部进行插入和删除操作。

Redis 使用的链表结构如下:

typedef struct listNode {
    struct listNode *prev;  // 前一个节点
    struct listNode *next;  // 后一个节点
    void *value;            // 节点的值
} listNode;

typedef struct list {
    listNode *head;         // 头节点
    listNode *tail;         // 尾节点
} list;

双向链表的设计使得 List 操作非常高效,尤其是在实现先进先出的队列时。

4.2 压缩列表(Ziplist)

当 List 中的元素较少时,Redis 仍会选择使用 压缩列表(Ziplist) 以节省内存。与 Hash 的情况类似,Ziplist 在节省内存的同时也会牺牲一定的性能。

Set 类型及底层实现

Set 是无序的集合,集合中的元素是唯一的,且不允许重复。Set 非常适合存储不重复的数据,如标签系统、好友关系等。

5.1 哈希表

当 Set 中的元素数量较多时,Redis 使用 哈希表 来存储集合。由于 Set 不需要存储值,只需要确保元素的唯一性,哈希表在这里仅使用键来保证数据的唯一性。

5.2 整数集合(Intset)

当 Set 中存储的元素是整数且数量较少时,Redis 会使用 整数集合(Intset) 来存储。这是一种紧凑型的数据结构,专门用来存储小范围的整数集合。

整数集合的结构如下:

typedef struct intset {
    uint32_t encoding;      // 编码方式
    uint32_t length;        // 集合中的元素个数
    int8_t contents[];      // 存储整数的数组
} intset;

整数集合根据元素大小自动选择不同的编码方式,以提高存储效率。

Sorted Set 类型及底层实现

Sorted Set 是 Redis 中的有序集合,集合中的每个元素都有一个分数,元素按照分数进行排序。Sorted Set 常用于排行榜系统、按权重排序的数据等场景。

6.1 跳表(SkipList)

当 Sorted Set 的元素较多时,Redis 使用 跳表(SkipList) 结构来实现。跳表是一种可以在 O(log N) 时间复杂度内进行插入、删除和查找的有序数据结构。

Redis 中的跳表结构如下:

typedef struct zskiplist {
    struct zskiplistNode *header;  // 跳表头节点
    struct zskiplistNode *tail;    // 跳表尾节点




    unsigned long length;          // 跳表长度
    int level;                     // 跳表层数
} zskiplist;

跳表通过多层级链表来加快数据的查找速度,是一种内存友好的排序结构。

6.2 压缩列表(Ziplist)

当 Sorted Set 中的元素较少时,Redis 也可以使用 压缩列表(Ziplist) 来存储。这与 Hash 和 List 的实现类似,用以节省内存。

总结

Redis 提供了五种主要的数据类型,每种类型都有其特定的应用场景和底层数据结构。在实际使用中,选择合适的数据类型和理解其底层实现有助于优化 Redis 的性能。通过合理地设计和运用 Redis 的数据结构,我们可以在保证性能的前提下,最大化地利用 Redis 的优势。


原文地址:https://blog.csdn.net/fudaihb/article/details/142649810

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!