vault backup: 2026-05-25 20:51:57

This commit is contained in:
hhs
2026-05-25 20:51:57 +08:00
parent 383d98a4dc
commit 7e4671fe25
8 changed files with 1673 additions and 43 deletions
+53 -3
View File
@@ -26,6 +26,22 @@ Redis 提供五种基础数据类型,用一句话总结各自的核心价值
>
> 了解编码不是为了手动管理它,而是帮你**预判性能和内存消耗**,避免踩坑(比如超大 Hash 的 `HGETALL` 阻塞)。
### 前置概念:底层编码结构速览
> [!tip] 这一节是"词典",不用现在全记住
> 下面六个概念会在后续各节的「深入」部分反复出现。先混个脸熟,遇到时回来查即可。
| 结构 | 一句话定义 | 用在哪里 |
|------|-----------|---------|
| **ziplist** | 连续内存的紧凑列表,省空间但查找 O(N) | Hash / List(quicklist 节点) / ZSet(小数据量) |
| **listpack** | ziplist 的继任者,去掉了 prevlen 彻底解决级联更新 | Redis 7.2+ 替代 ziplist |
| **hashtable** | 标准哈希表,查找 O(1) | Hash / Set / ZSet 的编码之一 |
| **intset** | 有序整数数组,纯整数集合时使用 | Set(全为整数时) |
| **quicklist** | 双向链表,每个节点是一个 ziplist/listpack | List 的统一编码 |
| **skiplist** | 多层索引的有序链表,范围查询 O(logN) | ZSet(大数据量时) |
> 想深入了解? [[02-核心数据类型/02-1-ziplist与listpack|ziplist 与 listpack]] · [[02-核心数据类型/02-2-skiplist|skiplist]] · [[02-核心数据类型/02-3-hashtable|hashtable]] · [[02-核心数据类型/02-4-quicklist|quicklist]]
我们先从最简单的 String 开始,逐个击破。
## 一、String(字符串)
@@ -155,6 +171,8 @@ List 统一使用 **quicklist**,每个节点是一个 ziplist:
- `list-max-ziplist-size 5`:默认每个节点最多 4096 个元素
- `list-compress-depth 0`:默认不压缩首尾节点(方便头部操作)
> 想深入了解? → [[02-核心数据类型/02-4-quicklist|quicklist 结构详解]]
## 四、Set(集合)
Set 就是**不重复的集合**——同一个元素加多少次都只保留一份。它的杀手锏是**集合运算**:交集、并集、差集。
@@ -186,18 +204,46 @@ SUNION tags:post1 tags:post2 # → go golang redis docker — 所有标签
### 深入:intset 编码
当 Set 里全是整数时,Redis 会用 **intset**(有序整数数组)存储,内存极其紧凑。一旦加入非整数元素,自动切换为 **hashtable**。
当 Set 里**全是整数**时,Redis 会用 **intset**(整数集合)存储。它的本质是一块**连续的、有序的整数数组**,支持二分查找(O(logN)),内存极其紧凑——没有指针、没有哈希桶,每个元素只占它自身的字节数。
> [!question] intset 比 hashtable 省多少?
> 存 1000 个整数:hashtable 每个 `dictEntry` 至少 24 字节(key 指针 + value 指针 + next),总计 ~24KB+。intset 用 `int32` 存储只需 4KB——**省了 80%**。
**intset 内存布局**:
```mermaid
flowchart LR
EN["encoding, 4B, 16/32/64 位"] --> LN["length, 4B, 元素个数"]
LN --> C1["contents, 有序排列的整数数组"]
classDef header fill:#e1f5fe,stroke:#2196f3
classDef data fill:#fff3e0,stroke:#ff9800
class EN,LN header
class C1 data
```
**自动升级机制**:当插入一个比当前编码更大的整数时,intset 会**升级**——把所有元素扩展到更宽的类型(如 `int16` → `int32`),然后插入新元素。这是单向的,**不会自动降级**。
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `set-max-intset-entries` | 512 | 元素数超过 512 → 切换为 hashtable |
> [!caution] intset 一旦遇到非整数就"永久"切换
> 加入一个字符串后,整个 Set 切成 hashtable,即使后来删掉了那个字符串,**也不会切回 intset**。所以如果你的 Set 明确只存整数(如用户 ID 集合),别手滑加字符串。
```mermaid
flowchart LR
E["Set 添加元素"] --> Q{"全部是整数?"}
Q -->|"是"| I["intset, 有序数组, 内存紧凑"]
Q -->|"否"| H["hashtable, O(1) 查找"]
Q -->|"是"| Q2{"元素数 <= 512?"}
Q2 -->|"是"| I["intset, 有序数组, 内存极紧凑"]
Q2 -->|"否"| H["hashtable, O(1) 查找"]
Q -->|"否"| H
classDef encoding fill:#e1f5fe,stroke:#2196f3
classDef encoding2 fill:#fff3e0,stroke:#ff9800
classDef check fill:#e8f5e9,stroke:#4caf50
class I encoding
class H encoding2
class Q,Q2 check
```
> [!tip] SSCAN 避免阻塞
@@ -294,6 +340,10 @@ Redis 在底层用不同的编码策略(ziplist、intset、skiplist 等)来*
## 关联笔记
- [[02-核心数据类型/02-1-ziplist与listpack]] — 底层编码:ziplist 内存布局与级联更新
- [[02-核心数据类型/02-2-skiplist]] — 底层编码:skiplist 原理与 ZSet 双索引设计
- [[02-核心数据类型/02-3-hashtable]] — 底层编码:dict 结构与渐进式 rehash
- [[02-核心数据类型/02-4-quicklist]] — 底层编码:quicklist 结构与 LZF 压缩
- [[hhs/Redis/08-SortedSet精解]] — ZSet 的高级玩法
- [[hhs/Redis/03-基本命令速查]] — 常用命令速查表
- [[hhs/Redis/07-集群方案]] — 集群环境下的大 Key 风险