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 同时维护跳表+哈希表
相关推荐
panpan16191 小时前
Redis命令大全
redis
java1234_小锋3 小时前
Redis 宣布正式接入 AI
数据库·人工智能·redis
行百里er3 小时前
Redis 持久化——RDB 快照 vs AOF 日志
redis·后端
程序猿乐锅4 小时前
一文讲透缓存穿透、击穿和雪崩
java·redis·spring·缓存·mybatis
程序猿乐锅5 小时前
【黑马点评 | 第七篇】Redis 分布式锁的两种实现
java·数据库·redis·分布式·spring·缓存
程序猿乐锅7 小时前
【黑马点评 | 第九篇】秒杀优化-异步实现
java·spring boot·redis·后端·spring·中间件·mybatis
黄金龙PLUS9 小时前
5个1024比特大状态置换算法的设计与分析
算法·网络安全·密码学·哈希算法·同态加密
旋生万物10 小时前
Redis 缓存雪崩?不是 TTL 一样,是过期相位同步塌陷(附打散脚本)
redis·缓存·aigc·螺旋生成论·螺旋相位
夕除12 小时前
redis--020
redis
不好听61318 小时前
缓存三大坑:穿透·击穿·雪崩一次讲透
redis