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

目录

  1. [Redis 数据类型概览](#Redis 数据类型概览)
  2. [String 类型及底层实现](#String 类型及底层实现)
    • 2.1 简单动态字符串(SDS)
  3. [Hash 类型及底层实现](#Hash 类型及底层实现)
    • 3.1 哈希表(Hash Table)
    • 3.2 压缩列表(Ziplist)
  4. [List 类型及底层实现](#List 类型及底层实现)
    • 4.1 双向链表(LinkedList)
    • 4.2 压缩列表(Ziplist)
  5. [Set 类型及底层实现](#Set 类型及底层实现)
    • 5.1 哈希表
    • 5.2 整数集合(Intset)
  6. [Sorted Set 类型及底层实现](#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 的结构如下:

c 复制代码
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,其主要结构如下:

c 复制代码
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 使用的链表结构如下:

c 复制代码
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) 来存储。这是一种紧凑型的数据结构,专门用来存储小范围的整数集合。

整数集合的结构如下:

c 复制代码
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 中的跳表结构如下:

c 复制代码
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 的优势。

相关推荐
夜泉_ly2 小时前
MySQL -安装与初识
数据库·mysql
qq_529835353 小时前
对计算机中缓存的理解和使用Redis作为缓存
数据库·redis·缓存
月光水岸New5 小时前
Ubuntu 中建的mysql数据库使用Navicat for MySQL连接不上
数据库·mysql·ubuntu
狄加山6755 小时前
数据库基础1
数据库
我爱松子鱼5 小时前
mysql之规则优化器RBO
数据库·mysql
chengooooooo5 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
Rverdoser6 小时前
【SQL】多表查询案例
数据库·sql
Galeoto7 小时前
how to export a table in sqlite, and import into another
数据库·sqlite
希忘auto7 小时前
详解Redis在Centos上的安装
redis·centos
人间打气筒(Ada)7 小时前
MySQL主从架构
服务器·数据库·mysql