Redis--基础知识点--33--String Hash List Set ZSet

类型 底层数据结构 编码切换条件
String int / embstr / raw 按值大小和类型切换
Hash listpack / hashtable 元素少用 listpack,多了用 hashtable
List quicklist(7.0+) 历史用过 ziplist + linkedlist
Set intset / hashtable 全整数用 intset,否则 hashtable
ZSet listpack / skiplist+hashtable 元素少用 listpack,多了用 skiplist

String

复制代码
值类型判断:
├── 整数 → int 编码
├── 字符串 ≤ 44 字节 → embstr 编码
└── 字符串 > 44 字节 → raw 编码
编码 底层结构 条件 说明
int long 整数 值为整数且 ≤ long 范围 直接存 SDS header + 数字
embstr SDS + RedisObject 连续内存 字符串 ≤ 44 字节 一次内存分配,缓存友好
raw SDS + RedisObject 分离内存 字符串 > 44 字节 两次内存分配
复制代码
embstr:  [RedisObject | SDS header | data]  ← 一块连续内存
raw:     [RedisObject] → 指针 → [SDS header | data]  ← 两块内存

44 字节阈值原因:64 字节 jemalloc 分配,减去 RedisObject(16B) + SDS header(3B) + 结尾 \0(1B) + 预留(4B) ≈ 44B


Hash

编码 底层结构 切换条件
listpack 紧凑连续存储 元素数 < 128 且 每个 entry < 64 字节
hashtable 哈希表 超过任一阈值则切换
复制代码
listpack 编码:
┌─────────────────────────────────┐
│ field1 | value1 | field2 | value2 │  ← 连续内存,省空间
└─────────────────────────────────┘

hashtable 编码:
┌──────────┐
│ dictEntry │ → field → value
│ dictEntry │ → field → value
│ ...       │
└──────────┘
特性 listpack hashtable
内存占用 极小 有额外指针开销
查找复杂度 O(n) O(1)
适用场景 少量元素 大量元素

List

版本 底层结构 说明
Redis 7.0+ quicklist 双向链表,每个节点是一个 listpack
Redis 3.2~6.x quicklist 双向链表 + ziplist 节点
Redis < 3.2 ziplist / linkedlist 按条件切换
复制代码
quicklist 结构:

  head ←→ [listpack node] ←→ [listpack node] ←→ [listpack node] ←→ tail
            ┌──────────┐     ┌──────────┐
            │ item1    │     │ item4    │
            │ item2    │     │ item5    │
            │ item3    │     │ item6    │
            └──────────┘     └──────────┘
           (连续内存块)      (连续内存块)
特性 说明
兼顾内存和性能 小数据用 listpack 压缩省内存,大数据用链表保证操作效率
单节点 listpack 大小 默认 8KB,可配置 list-max-listpack-size
支持 LPOP/RPOP O(1)
支持 LRANGE O(n)

Set

编码 底层结构 切换条件
intset 有序整数数组 所有元素都是整数 且 元素数 ≤ 512
hashtable 哈希表 非整数元素 或 元素数 > 512
复制代码
intset 编码:
┌───────────────────────────────────────────┐
│ header | 1 | 5 | 10 | 20  │  ← 有序整数数组,支持二分查找
└───────────────────────────────────────────┘
  升级: int16 → int32 → int64(按最大元素决定编码)

hashtable 编码:
┌──────────┐
│ dictEntry │ → "apple" (value=NULL)
│ dictEntry │ → "banana"
│ ...       │
└──────────┘
特性 intset hashtable
内存 极省(无指针开销) 有指针开销
查找 O(log n) 二分查找 O(1)
元素类型 仅整数 任意
编码升级 int16→int32→int64 ---

ZSet(Sorted Set)

编码 底层结构 切换条件
listpack 紧凑连续存储 元素数 < 128 且 每个 entry < 64 字节
skiplist + hashtable 跳表 + 哈希表 超过任一阈值
复制代码
skiplist 编码(双结构组合):

  ┌─────────────────────────────────────────────────┐
  │ SkipList(跳表)                                 │
  │                                                  │
  │  L3:  head ──────────────────→ 30               │
  │  L2:  head ─────→ 10 ───────→ 30               │
  │  L1:  head → 5 → 10 → 20 ──→ 30                │
  │  L0:  head → 5 → 10 → 20 → 30 → 40             │
  │                                                  │
  │  每个 node: [member, score, forward[], backward] │
  └─────────────────────────────────────────────────┘

  ┌─────────────────────────────────────────────────┐
  │ Dict(哈希表)                                   │
  │                                                  │
  │  "apple"  → score=10                            │
  │  "banana" → score=20                            │
  │  "cherry" → score=5                             │
  │                                                  │
  │  作用: O(1) 查 score                            │
  └─────────────────────────────────────────────────┘
操作 用哪个结构 复杂度
ZADD 跳表插入 + 哈希表更新 O(log n)
ZSCORE 哈希表 O(1)
ZRANK 跳表回溯 O(log n)
ZRANGE 跳表遍历 O(log n + m)

为什么用两个结构? 跳表支持范围查询,哈希表支持 O(1) 查分数。两者配合覆盖所有操作。


为什么用跳表而不是红黑树?

对比 跳表 (SkipList) 红黑树 (RBTree)
范围查询 ✅ 天然支持,链表顺序遍历 ❌ 需要中序遍历,复杂
实现复杂度 简单(概率性平衡) 复杂(旋转+变色)
并发友好 ✅ 局部加锁 ❌ 旋转影响范围大
内存 额外多级指针 每节点额外颜色位
缓存友好 较好 一般

Redis 作者 Antirez 的选择理由:实现简单 + 范围查询天然支持 + 并发友好


总结速查

复制代码
String:  int / embstr / raw
Hash:    listpack / hashtable
List:    quicklist
Set:     intset / hashtable
ZSet:    listpack / skiplist + hashtable
设计原则 体现
小数据省内存 listpack / intset 紧凑存储
大数据保性能 hashtable / skiplist 保证 O(1) 或 O(log n)
编码自动切换 根据元素数量和大小自动升级,对用户透明
空间换时间 ZSet 同时维护跳表+哈希表
相关推荐
三8442 小时前
webshell缓存绕过/哈希碰撞
算法·哈希算法
2602_959960923 小时前
大厂Java面试:Spring Boot、JVM、Redis、Kafka与Elasticsearch在内容社区UGC场景下的实战拷问
java·jvm·spring boot·redis·面试题
杨运交11 小时前
[060][调度模块]Redisson vs Redis 原生锁:两种分布式锁实现深度对比
数据库·redis·分布式
何以解忧,唯有..11 小时前
深入理解与应对:Redis 缓存雪崩、击穿、穿透三大经典问题
redis·缓存·mybatis
何以解忧,唯有..17 小时前
Redis 五种核心数据类型详解与经典使用场景总结
数据库·redis·缓存
xrkhy21 小时前
redis-shake的使用windows版本
数据库·windows·redis
星恒随风1 天前
C++ 哈希详解(二):开放定址、哈希桶与 C++ 哈希表底层实现
c++·笔记·学习·哈希算法·散列表
lingran__1 天前
C++ STL unordered系列(哈希) 底层剖析与模拟实现万字详解 | 基于哈希表,复刻 SGI-STL 泛型哈希容器架构
开发语言·c++·后端·哈希算法·哈希表·泛型编程·unordered系列