Update Redis.md

补充对象编码,以及 Redis Cluster 的哈希槽、重定向、哈希标签和故障转移,并配图。
This commit is contained in:
CyC2018
2026-10-26 02:34:35 +08:00
parent e2c554d7de
commit 91676af01c
3 changed files with 104 additions and 0 deletions
+35
View File
@@ -9,6 +9,7 @@
* [HASH](#hash)
* [ZSET](#zset)
* [三、数据结构](#三数据结构)
* [对象编码](#对象编码)
* [字典](#字典)
* [跳跃表](#跳跃表)
* [四、使用场景](#四使用场景)
@@ -39,6 +40,7 @@
* [主从链](#主从链)
* [十二、Sentinel](#十二sentinel)
* [十三、分片](#十三分片)
* [Redis Cluster](#redis-cluster)
* [十四、一个简单的论坛系统分析](#十四一个简单的论坛系统分析)
* [文章信息](#文章信息)
* [点赞功能](#点赞功能)
@@ -209,6 +211,20 @@ OK
## 三、数据结构
### 对象编码
同一种数据类型会根据元素的数量和大小选择不同的底层结构,称为编码,可以用 OBJECT ENCODING 查看。元素少时使用 listpack,它把所有元素连续地存放在一块内存中,节省内存,查找时虽然要逐个比较,但元素很少时并不慢;元素增多后转换为查找更快的结构。以下是在 Redis 7.4 上用默认配置验证的结果:
| 类型 | 元素少时 | 超过阈值后 | 阈值 |
| --- | --- | --- | --- |
| STRING | int(整数)、embstr | raw | 44 字节 |
| LIST | listpack | quicklist(多个 listpack 组成的链表) | 单个 listpack 超过 8 KB |
| HASH | listpack | hashtable | 512 个字段,或者有值超过 64 字节 |
| SET | intset(全是整数)、listpack | hashtable | 整数 512 个,其它 128 个 |
| ZSET | listpack | skiplist(跳跃表 + 字典) | 128 个成员,或者有成员超过 64 字节 |
例如一个 HASH 有 512 个字段时编码为 listpack,再写入一个字段就变为 hashtable。ZSET 转换后同时使用跳跃表和字典:字典按成员查分数,跳跃表按分数排序和范围查询。
### 字典
dictht 是一个散列表结构,使用拉链法解决哈希冲突。
@@ -570,6 +586,24 @@ Sentinel(哨兵)可以监听集群中的服务器,并在主服务器进入
- 代理分片:将客户端请求发送到代理上,由代理转发请求到正确的节点上。
- 服务器分片:Redis Cluster,使用 CRC16(key) % 16384 计算键所属的哈希槽,再把 16384 个哈希槽分配给各个节点。
### Redis Cluster
Redis 有三种常见的部署方式,解决的问题不同:
- 主从复制:从服务器复制主服务器的数据,分担读请求,主服务器宕机后需要手动切换;
- Sentinel:在主从复制的基础上自动监控和故障转移,但仍然只有一个主服务器负责写入,数据量受单机内存限制;
- Redis Cluster:把数据分散到多个主节点上,每个主节点有自己的从节点,同时解决容量和高可用的问题。
Redis Cluster 把键空间分为 16384 个哈希槽,每个主节点负责一部分槽。下面是在本机创建的 3 主 3 从的集群:
<div align="center"> <img src="pics/redis-cluster.png" width="720px"/> </div><br>
- 客户端向不负责这个槽的节点发送命令时,会收到 MOVED 重定向。例如 user:1001 所在的槽 5712 属于 7002,发给 7001 会收到 MOVED 5712 127.0.0.1:7002。客户端会缓存槽与节点的对应关系,之后直接访问正确的节点;
- 一条命令涉及多个键时,这些键必须在同一个槽中,否则报错 CROSSSLOT,Lua 脚本也是如此。可以用哈希标签(hash tag)让相关的键落到同一个槽:只有 {} 中的部分参与计算,{user:1001}:cart 和 {user:1001}:orders 都在槽 5712;
- 主节点宕机后,它的一个从节点会被选为新的主节点。在本机把 cluster-node-timeout 设为 5 秒,停掉一个主节点后,这部分槽约 9 秒无法写入。
需要注意的是,主从之间是异步复制,主节点宕机前刚写入、还没有复制到从节点的数据会丢失,所以 Redis Cluster 不保证强一致性。
## 十四、一个简单的论坛系统分析
该论坛系统功能如下:
@@ -610,3 +644,4 @@ Redis 没有关系型数据库中的表这一概念来将同种类型的数据
- [Redis 3.0 中文版- 分片](http://wiki.jikexueyuan.com/project/redis-guide)
- [Redis 应用场景](http://www.scienjus.com/redis-use-case/)
- [Using Redis as an LRU cache](https://redis.io/topics/lru-cache)
- [Redis: Scale with Redis Cluster](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling/)
Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

+69
View File
@@ -0,0 +1,69 @@
<svg xmlns="http://www.w3.org/2000/svg" width="720" height="340" viewBox="0 0 720 340">
<style>
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #666; font-size: 12px; }
.title { font-weight: bold; font-size: 15px; fill: #777; text-anchor: middle; }
.b { font-weight: bold; }
.c { text-anchor: middle; }
.e { text-anchor: end; }
.note { fill: #C0605A; }
.ok { fill: #6A9F68; }
.sub { font-size: 11px; fill: #888; }
.mono { font-family: Menlo, Consolas, monospace; }
.arrow { stroke: #666; stroke-width: 1.3; fill: none; marker-end: url(#a); }
.red { stroke: #C0605A; marker-end: url(#r); }
.green { stroke: #6A9F68; marker-end: url(#g); }
.dash { stroke-dasharray: 5 4; }
.line { stroke: #666; stroke-width: 1.3; fill: none; }
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
.shadow { fill: #DADADA; }
</style>
<defs>
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" fill="none" stroke="#666" stroke-width="1.6"/>
</marker>
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" fill="none" stroke="#C0605A" stroke-width="1.6"/>
</marker>
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" fill="none" stroke="#6A9F68" stroke-width="1.6"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#FFFFFF"/>
<text x="360" y="28" class="title">Redis Cluster:3 主 3 从</text>
<rect x="22" y="54" width="210" height="54" rx="6" class="shadow"/>
<rect x="20" y="52" width="210" height="54" rx="6" fill="#DDD7F6"/>
<text x="125" y="74" class="c b mono">user:1001</text>
<text x="125" y="94" class="c sub mono">CRC16(key) % 16384 = 5712</text>
<rect x="252" y="152" width="140" height="52" rx="6" class="shadow"/>
<rect x="250" y="150" width="140" height="52" rx="6" fill="#B1DDF0"/>
<text x="320" y="174" class="b c">主节点 7001</text>
<text x="320" y="189" class="sub c">槽 0~5460</text>
<rect x="262" y="252" width="120" height="36" rx="6" class="shadow"/>
<rect x="260" y="250" width="120" height="36" rx="6" fill="#E6E6E6"/>
<text x="320" y="272.5" class="b c">从节点</text>
<line x1="320" y1="205" x2="320" y2="246" class="arrow dash"/>
<rect x="402" y="152" width="140" height="52" rx="6" class="shadow"/>
<rect x="400" y="150" width="140" height="52" rx="6" fill="#B1DDF0"/>
<text x="470" y="174" class="b c">主节点 7002</text>
<text x="470" y="189" class="sub c">槽 5461~10922</text>
<rect x="412" y="252" width="120" height="36" rx="6" class="shadow"/>
<rect x="410" y="250" width="120" height="36" rx="6" fill="#E6E6E6"/>
<text x="470" y="272.5" class="b c">从节点</text>
<line x1="470" y1="205" x2="470" y2="246" class="arrow dash"/>
<rect x="552" y="152" width="140" height="52" rx="6" class="shadow"/>
<rect x="550" y="150" width="140" height="52" rx="6" fill="#B1DDF0"/>
<text x="620" y="174" class="b c">主节点 7003</text>
<text x="620" y="189" class="sub c">槽 10923~16383</text>
<rect x="562" y="252" width="120" height="36" rx="6" class="shadow"/>
<rect x="560" y="250" width="120" height="36" rx="6" fill="#E6E6E6"/>
<text x="620" y="272.5" class="b c">从节点</text>
<line x1="620" y1="205" x2="620" y2="246" class="arrow dash"/>
<text x="325" y="236" class="sub">异步复制</text>
<path d="M232,92 C330,96 440,110 470,146" class="arrow"/>
<text x="380" y="104" class="c sub">槽 5712 在 7002</text>
<text x="20" y="128" class="sub note">如果发给 7001:MOVED 5712 127.0.0.1:7002</text>
<path d="M100,136 C100,166 200,176 246,176" class="arrow red dash"/>
<text x="20" y="312" class="sub">{user:1001}:cart 和 {user:1001}:orders 只按 {} 中的部分计算,都在槽 5712,可以在同一条命令或 Lua 脚本中使用</text>
<text x="20" y="330" class="sub">主节点宕机后,从节点被选为新的主节点;本机 cluster-node-timeout 为 5 秒时,这部分槽约 9 秒无法写入</text>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB