KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

00 · 三套部署形态:先决定该上哪一套 — keel 龙骨

在动手配任何参数之前,先把三套形态解决的问题分清楚。它们经常被当成"由小到大的三个阶段",但其实是三个不同维度:读写分离解决读压力,哨兵解决主库单点,集群解决单机容量上限。选错的代价不是配置白写,而是架构返工。

在动手配任何参数之前,先把三套形态解决的问题分清楚。它们经常被当成"由小到大的三个阶段",但其实是三个不同维度:读写分离解决读压力,哨兵解决主库单点,集群解决单机容量上限。选错的代价不是配置白写,而是架构返工。


一、现场:一次「因为读压力大所以上集群」的返工

某内容站点的 Redis 监控显示:内存 12G / 32G、QPS 3 万(其中 90% 是读)、CPU 60%。结论是"快到顶了,上集群"。搭完 3 主 3 从之后出了三件事:

  1. 批量取缓存的 MGET 大面积报 CROSSSLOT Keys in request don't hash to the same slot;
  2. ARQ 的任务脚本一次要碰 4 个 key,同样报错,任务全部堆积;
  3. 内存问题其实不存在——12G 里 9G 是可以过期淘汰的缓存,真正需要"容量"的数据只有 2G。

真正的瓶颈是读 QPS,正确解法是「一主两从 + 读写分离」,工作量是搭集群的十分之一,而且不需要改一行业务代码。

这一章的结论先摆在这里:形态的选型依据是瓶颈的性质,不是"数据量大了就上集群"。

二、概念边界:三套形态各自解决什么、不解决什么

形态 解决什么 不解决什么 数据份数
主从复制 读扩展(把读分摊到副本)、数据冗余 主库挂了谁来接(没有自动切换) 每个副本一份全量
主从 + 哨兵 主库单点:自动判定、自动提升、通知客户端 容量(所有节点仍是全量数据)、写入安全 每个副本一份全量
集群 Cluster 单机容量与写吞吐瓶颈:把数据分片到多组节点 跨槽多键操作、跨槽事务;也不解决单 key 热点 每个分片一份,分片之间互补

三条容易搞混的边界:

三、选型决策图

flowchart TD
    A["先定位瓶颈<br/>(看监控,不要猜)"] --> B{"数据量超过单机<br/>可用内存?"}
    B -- 是 --> C{"能否接受跨槽<br/>多键 / 事务 / Lua 受限?"}
    C -- 能 --> D["上集群 Cluster<br/>每个分片配 1~2 个副本"]
    C -- 不能 --> E["按角色拆实例<br/>(缓存一套、队列一套、锁一套)<br/>或改造 key 使用 hash tag"]
    B -- 否 --> F{"瓶颈是读 QPS<br/>还是写 QPS?"}
    F -- 读 QPS --> G["一主多从 + 读写分离<br/>从库数量按读量线性加"]
    F -- 写 QPS --> H{"写压力来自<br/>单 key 热点还是总量?"}
    H -- 总量 --> D
    H -- 单 key 热点 --> I["分片解决不了<br/>改 key 设计 / 本地缓存 / 限流"]
    G --> J{"主库挂了要<br/>自动恢复吗?"}
    J -- 要 --> K["加哨兵:至少 3 个<br/>跨机器部署"]
    J -- 不要 --> L["人工切换<br/>(有值守的内部系统可接受)"]
    D --> M["分片自带切换<br/>仍需监控槽覆盖与迁移状态"]

图上有两个最容易跳过的判定:

四、一次完整运行:本课程的实验环境

后面每章的实验都跑在同一套本机环境上,你可以照着搭一个:

Redis 5.0.14.1(Windows 构建,tporadowski 移植版)
  6379  主库
  6380  从库 A
  6381  从库 B
  26379 哨兵(第 03 章)
  7000 / 7001 / 7002  集群三主(第 05、06 章)

客户端:redis-py 8.1.0

启动三个数据节点(配置只给端口和工作目录,其余用默认值,便于观察默认行为):

redis-server --port 6379 --save "" --appendonly no --dir ./n6379
redis-server --port 6380 --save "" --appendonly no --dir ./n6380
redis-server --port 6381 --save "" --appendonly no --dir ./n6381

建立复制关系只需一条命令(第 01 章会解释它触发了什么):

redis-cli -p 6380 REPLICAOF 127.0.0.1 6379
redis-cli -p 6381 REPLICAOF 127.0.0.1 6379

两个版本坑,先记下来(后面章节还会遇到):

  1. 术语:Redis 5 的日志和 INFO 字段仍写 slave(slave0、slave_repl_offset、connected_slaves),Redis 7 起改为 replica。命令用新写法 REPLICAOF(SLAVEOF 仍可用),输出保留实测原样。

  2. 协议:redis-py 5+ 默认尝试 RESP3 握手(HELLO 3),而 Redis 5 没有 HELLO 这条命令,连接会直接失败:

    redis.exceptions.ResponseError: unknown command `HELLO`, with args beginning with: `3`
    

    本课所有 Python 示例因此显式传 protocol=2(顺带一提:哨兵客户端还要单独给"连哨兵那条连接"传一次,见第 03 章)。你连 Redis 6+ 时这两处都可以删掉。

五、主动破坏:把选型结论反过来验一次

拿你自己系统里的 Redis 用法,问三个问题。任何一问答不出来,就说明当前的形态是"碰巧能用",不是"选对的":

  1. 这个 key 所在的实例现在挂了,会发生什么? 如果答案是"不知道"或"全部重建",说明它的可用性要求从来没被满足过(通常是队列类 key)。
  2. 如果现在把读全部切到从库,有没有哪个查询会因为读到旧值而出错? 有,就必须给它标记"读主"(第 02 章)。
  3. 这台机器上所有 key 加起来的内存,能不能塞进一台机器? 如果能,集群带来的跨槽限制就是纯成本。

六、生产环境怎样替换

教学替身 生产替换 要点
本机三个端口 跨机器的主从,至少跨可用区 同机多实例只用来验证机制,不提供任何真实可用性
默认配置 显式写 replicaof、requirepass / masterauth(Redis 6+ 用 ACL) 只用命令配的复制关系,重启即失效
无监控 采集复制延迟、连接状态、切换事件(第 07 章清单) 没有监控,"高可用"只是自我感觉
Windows 构建 Linux 官方 Redis 本课用的 Windows 构建跑哨兵会崩(内存分配缺陷),不能当部署目标

七、练习与验收

练习 1(形态归类):列出你系统的 Redis key,按「能不能丢 / 能不能读旧值 / 会不会跨 key 操作」三列分类,指出每一类适合放在哪种形态的实例上。

练习 2(反例论证):找一个"上了集群但其实只需要主从"或"只配了主从但主库挂了没人管"的真实案例(自己的或公开的),写出它的瓶颈性质与正确形态。

验收点:不看资料,画出第 03 节的决策图,并解释为什么"单 key 热点"不能被分片解决。


现在能解释什么:三套形态各自解决哪个维度的问题、不解决什么;选型依据是瓶颈性质而不是数据量;同一实例混装不同敏感度的数据会带来什么取舍。

下一步:第 01 章把最底下那块砖——主从复制——拆开看它是怎么跑起来的。

进入 keel 阅读