Redis的数据类型 + 底层实现:String、Hash、List、Set、ZSet

本文的思维导图如下:

一、数据类型

注意:数据结构 ≈ 数据类型

Redis 提供了丰富的数据类型,常见的有 五种基本类型

  • String(字符串)
  • Hash(哈希)
  • List(列表)
  • Set(集合)
  • Zset(有序集合)

随着 Redis 版本的更新,后面又支持了四种高级类型

  • BitMap(2.2 版新增)
  • HyperLogLog(2.8版新增)
  • GEO(3.2 版新增)
  • Stream(5.0 版新增)

(一)String

1. 是什么?

String(字符串)是 Redis 中最基本的数据类型、key-value 结构。

key 是唯一标识,value 是具体的值,value 可以存储任意类型的数据,比如文本、数字和二进制数据,value 的最大长度为512MB。

2. 底层结构

Redis 的 String 的底层数据结构:动态字符串 SDS(Simple Dynamic String)

SDS 结构包含:

plsql 复制代码
len      当前字符串长度
alloc    已分配的空间
flags    类型标识
buf[]    真正的字符串内容

📌 特点:

  • 二进制安全(可以存任意数据)
  • 自动扩容 / 不会溢出
  • 支持 O(1) 获取长度(因为有 len 字段)

字符串对象的内部编码(encoding)有3种:int、raw和 embstr。

3. 使用场景

String 类型主要适用于简短的字符场景 ,其应用场景包括 缓存对象、常规计数、分布式锁、共享 session 信息等。

  • 缓存: 存储临时数据,如用户会话、页面缓存。
  • 计数器: 用于统计访问量、点赞数等,通过原子操作增加或减少。

(二)Hash

1. 是什么?

哈希是一个键值对集合,适合存储对象的属性。Redis内部使用哈希表 实现,适合小规模数据。

Hash = 键值对集合(field → value)

2. 底层结构

Redis 会根据规模选择两种结构:

1)小数据量 → listpack(Redis 7.0 前是 ziplist)

触发条件(默认):

  • field 和 value 都较小(长度 < 64 字节)
  • 键值对数量 < 512

底层结构:listpack(以前是 ziplist)

📌 特点:

  • 连续内存
  • 节省空间
  • 小 hash 性能很好

2)大数据量 → 哈希表(dict)

键值对数量变大后 Hash 会自动转为:

dict(hash table)哈希表

字典在 Redis 中使用:

  • 数组 buckets
  • 链地址法(链表)解决冲突

并且使用:

  • 渐进式 rehash(分批迁移) ------ 避免一次性扩容导致卡顿

Hash 总结

数据量 底层结构
listpack(旧 ziplist)
哈希表 dict

3. 使用场景

Hash 主要适用于存储、读取、修改用户、商品等属性。

其业务场景比如存储商品详情,即存储商品的各个属性,方便快速检索。


(三)List

1. 是什么

列表是有序的字符串集合,支持从两端推入和弹出元素。

2. 底层结构

一句话总结:

早期 List 用 ziplist + linkedlist 两种,但Redis 3.2 后,List 底层彻底统一为 quicklist

List 的底层实现跟 Redis 的版本有关,我们可以分为两阶段来讲:

1)Redis 3.2 之前(早期版本)

List 的底层可能是两种结构之一:

  1. 双向链表(linkedlist)
  2. 压缩列表(ziplist)

Redis 会根据++列表的"大小"++ 和++"元素长度"++自动选择:

  • 当List 的 元素个数较少 ,并且 每个元素的字节长度较短 时,
    ➜ 使用 ziplist(压缩列表) ,优点是内存连续、比较省空间
  • 元素个数很多 或者 元素偏大 时,
    ➜ 使用 双向链表 ,优点是插入删除方便,但指针多、内存开销相对更大

List 的元素个数list-max-ziplist-entries 配置,默认值是** 512** 个。

List 的元素字节长度list-max-ziplist-value 配置,默认值是 64 字节。

2)Redis 3.2 之后(后期版本)

Redis 3.2 开始,List 的底层实现就不再用「双向链表 + 压缩列表」二选一了,而是**统一改成 quicklist **。

我们可以把 quicklist 理解为:"双向链表 + 压缩列表"的组合体。

也就是说:

  • 外面是一条 双向链表
  • 链表的每个节点里,不是存一个元素,而是存 一个压缩列表(ziplist)

这样就兼顾了两类优点:

  1. 节省内存
    • 每个节点里用 ziplist 连着放很多元素,减少指针开销。
  2. 插入删除仍然高效
    • 在链表层面插入/删除节点是 O(1),
    • 在小的 ziplist 里移动元素成本也不高。
  3. 访问性能也不错
    • 比纯链表更 cache-friendly(因为 ziplist 是连续内存)。

List 总结

Redis 版本 底层结构
3.2 前 ziplist 或 linkedlist
3.2 以后 quicklist(唯一实现)

3. 使用场景

List 类型的应用场景有 消息队列、历史记录 等。

但是存在两个问题:

  1. 生产者需要自行实现全局唯一 ID;
  2. 不能以消费组形式消费数据。
  • 消息队列:
    • 用于简单任务调度、消息传递等场景
    • 通过LPUSH 和 RPOP 操作实现生产者消费者模式
    • 具体的有** 最新消息排行功能:**比如朋友圈的时间线
  • 历史记录: 存储用户操作的历史记录,便于快速访问。

(四)Set

1. 是什么

集合(Set)是无序且不重复 的字符串集合**,使用哈希表**实现,支持快速查找和去重操作。

Set(集合)存储 member,保证唯一性。

2. 底层结构

集合的底层分为:intset(整数集合)dict(哈希表)两种底层数据结构。

1)小整数集合 → intset

当:

  • 所有成员都是整数
  • 成员数量 < set-max-intset-entries(默认 512)

则使用 ------ intset

intset 是一个连续内存数组,内部自动升级:

  • 元素小 → 用 int16
  • 更大 → 自动升级为 int32、int64

2)大数据量或非整数 → dict

只要出现:

  • 非整数元素
  • 元素过多

立即使用------ dict

📌 特点:

  • 查找 O(1)
  • 保证唯一性

Set 总结

情况 底层结构
全是整数且数量少 intset
有非整数 或 数据量大 dict(哈希表)

3. 使用场景

Set 类型的使用场景有 聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动。

  • 标签系统: 存储用户的兴趣标签,避免重复。
  • 唯一用户集合: 记录访问过某个页面的唯一用户,方便进行分析。
  • 共同好友, 根据 tag 求交集。
  • 好友推荐时,根据 tag 求交集,大于某给阈值就可以推荐。

(五)ZSet

1. 是什么

ZSet = Sorted Set(有序集合),每个元素由:

sql 复制代码
member(成员) + score(分值)

ZSet 是一组++按关联分数++有序排列的字符串集合,这里的分数(Score)是个抽象概念,任何指标都可以抽象为分数,以满足不同的场景。

ZSet 里面的字符串是如何排序的?

2. 底层结构

Redis 会根据数据量自动选择底层结构:

1)小数据量 → listpack(Redis 7.0 之前叫 ziplist)

触发条件(默认):

  • 元素数量 < 128
  • 成员字符串长度 < 64 bytes

满足以上 → 使用 listpack

Redis 7.0 中,压缩列表 ziplist 已经废弃,改为 listpack。

其结构如下:

📌 特点:

  • 紧凑结构(连续内存),节省空间
  • 插入、遍历比较快
  • 适合小 ZSet

2)大数据量 → Skiplist + HashTable

不满足条件 → 使用 skiplist + hashtable 双结构

📌 为什么要两个结构?

结构 作用
Skiplist(跳表) 按 score 排序,支持范围查询 O(logN)
Hashtable(字典) 根据 member 快速查 score,时间复杂度 O(1)

▶ 跳表负责 "按 score 排序"

▶ Hash 表负责 "按 member 查找"

两者互补。

为什么要双结构:skiplist + hash?

ZSet 的操作有两类:

1)按 member 查找

如:

plsql 复制代码
ZSCORE key member  
ZREM key member

→ 需要 O(1) 哈希表查找。

2)按 score 排序或范围查询

如:

plsql 复制代码
ZRANGE key 0 -1
ZRANGEBYSCORE key 1 100
ZREVRANGE key ...

→ 跳表对范围查询、排序非常友好,能做到 O(logN)。

只有其中一种结构无法同时做到高性能

❌ 单用 hashtable

  • 无序
  • 不支持按 score 排序和范围查询

❌ 单用 skiplist

  • 查找 member 必须从头到尾扫描,O(N)

→ 因此必须:跳表负责排序,哈希表负责快速查找

总结表
Redis 版本 小数据结构 大数据结构
Redis 3.2 以前 ziplist skiplist + hashtable
Redis 3.2~6.x ziplist(更优化) skiplist + hashtable
Redis 7.0+ listpack(ziplist 替代者) skiplist + hashtable

3. 使用场景

Zset 类型的使用场景有 排序场景,比如排行榜、电话和姓名排序等。

  • 排行榜: 存储用户分数,实现实时排行榜,比如游戏排行榜。
  • 任务调度: 根据任务的优先级进行排序,方便调度执行。
结构类型 结构存储的值 结构的读写能力
String 字符串 可以是字符串、整数或浮点数 对整个字符串或字符串的一部分进行操作; 对整数或浮点数进行自增或自减操作;
Hash 哈希表 包含键值对的无序散列表 包含方法有添加、获取、删除单个元素
List 列表 一个链表,链表上的每个节点都包含一个字符串 对链表的两端进行 push 和 pop 操作,读取单个或多个元素;根据值查找或删除元素;
Set 集合 包含字符串的无序集合 字符串的集合,包含基础的方法有查看是否存在添加、获取、删除;还包含计算交集、并集、差集等
Zset 有序集合 和散列一样,用于存储键值对 字符串成员与浮点数分数之间的有序映射; 元素的排列顺序由分数的大小决定; 包含方法有添加、获取、删除单个元素以及根据分值范围或成员来获取元素

(六)BitMap

  • BitMap(2.2 版新增)
  • 常用于二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总数等。

(七)HyperLogLog

  • HyperLogLog(2.8 版新增)
  • 常用于海量数据基数统计的场景,比如百万级网页 UV 计数等。

(八)GEO

  • GEO(3.2 版新增)
  • 常用于存储地理位置信息的场景,比如滴滴叫车。

(九)Stream

  • Stream(5.0 版新增)
  • 常见的应用场景有:消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。

相关面试题

Redis 的数据类型有哪些?

提示:Redis 有 5 种基本类型 和 4 种高级类型,面试能回答出五种常见的基本类型就可以,其他新的数据类型则需学习。

Redis 常见的数据结构有五种:String(字符串)、List(列表)、Hash、Set(集合)、Zset(有序集合)。

Redis 有哪些底层数据结构?

Redis 主要数据结构的底层实现如下:

  • String 使用 SDS 实现,是动态可扩展的二进制安全字符串。
  • Hash 小数据时使用 listpack(旧版本 ziplist),大时使用哈希表 dict。
  • List 在 Redis 3.2 后统一由 quicklist 实现,即用双向链表串联多个 listpack 节点。
  • Set 小整数集合用 intset,否则使用 dict。
  • ZSet 小集合用 listpack,大集合用 skiplist + dict 双结构组合,以同时支持按 score 排序和按 member 查找。

String 的底层实现是什么?

早期:list = ziplist 或 linkedlist,根据元素个数和大小自动选择;

Redis 3.2 之后:List 底层统一由 quicklist 实现 , quicklist =「双向链表 +
压缩列表
」的混合结构,用链表串起多个小的压缩列表,在内存占用与操作性能之间做了折中。

参考:

Hash 的底层实现是什么?

参考:

List 的底层实现是什么?

参考:

Set 的底层实现是什么?

参考:

ZSet 的底层实现是什么?

ZSet 底层结构取决于数据量大小。

  • 当成员数量较少(默认小于 128)且成员较短(小于 64 字节)时,ZSet 底层采用 listpack(旧版本是 ziplist),节省内存。
  • 当数据量较大时,ZSet 使用 skiplist + hashtable 的双结构 : 哈希表根据 member 做 O(1) 查找;跳表按 score 排序并支持范围查询 O(logN)。 Redis 7.0 后 ziplist 被废弃,统一由 listpack
    实现。

这样的双结构可以同时支持按 score 排序和按 member 查找。

参考:

相关推荐
梦想的旅途21 小时前
企业微信二次开发中的零信任存储与传输加密实践
数据库
清水白石0081 小时前
什么是猴子补丁(Monkey Patch)?生产环境能用吗?——实战导读
python·安全·系统安全
say_fall1 小时前
C语言编程实战:每日一题:用队列实现栈
c语言·开发语言·redis
xiaoqi9766336901 小时前
免费文字转语音助手 python+edge_tts+FFMPEG
python·edge·ffmpeg
liupenglove1 小时前
go-echarts基础使用方法
开发语言·golang·echarts
APIshop1 小时前
用“爬虫”思路做淘宝 API 接口测试:从申请 Key 到 Python 自动化脚本
爬虫·python·自动化
rchmin1 小时前
阿里Canal数据库增量日志解析工具介绍
数据库·mysql
TDengine (老段)1 小时前
TDengine 字符串函数 GROUP_CONCAT 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
AI科技星1 小时前
时空的几何之歌:论统一场论动量公式 P = m(C - V) 的完备重构、量化哲学诠释与终极验证
数据结构·人工智能·算法·机器学习·计算机视觉·重构