KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

07 · 生产落地:监控指标、故障手册与四类角色的形态选择 — keel 龙骨

最后一章把前六章收成能直接用的东西:该盯哪些指标、出问题先查什么、以及回到第 00 章那四类 Redis 角色(缓存 / 队列 / 锁 / 事件流),逐个说明它们该配哪种形态、代码上要注意什么。

最后一章把前六章收成能直接用的东西:该盯哪些指标、出问题先查什么、以及回到第 00 章那四类 Redis 角色(缓存 / 队列 / 锁 / 事件流),逐个说明它们该配哪种形态、代码上要注意什么。


一、现场:报警响了,但不知道该看哪

凌晨收到"接口 P99 变高"。打开监控:CPU 正常、内存平稳、连接数不高。没有方向。

这类工单的共性是:监控采的是"资源指标",而出问题的是"拓扑状态"。复制延迟、槽覆盖、切换事件这些拓扑指标没采,就只能靠猜。

二、监控清单:按形态分组

通用(三套形态都要)

指标 来源 关注点
used_memory / maxmemory INFO memory 水位;配合 evicted_keys 看是否开始淘汰(命中率下降的前兆)
instantaneous_ops_per_sec INFO stats 吞吐基线,扩容判断依据
客户端侧 P99 延迟 客户端埋点 服务端命令耗时漏掉了网络与排队,必须客户端侧测
rejected_connections INFO stats 连接数打满
慢日志 SLOWLOG GET 大 key、KEYS、大集合操作的第一现场;从库也要采

主从 / 哨兵

指标 来源 告警建议
master_link_status INFO replication 不是 up 立即告警
connected_slaves 同上 少于预期说明有副本掉线
master_repl_offset - slave_repl_offset 同上(两侧相减) 复制延迟的真实量,用字节差而不是 lag 秒数
repl_backlog_histlen / repl_backlog_size 同上 接近 100% 说明即将触发全量重传
master_sync_in_progress 同上(从库) 为 1 时该从库正在加载 RDB,读服务质量下降
num-other-sentinels SENTINEL master <name> 少于 哨兵总数-1 说明有哨兵失联
flags 含 s_down / o_down 同上 出现即告警
+switch-master 事件 哨兵 pub/sub 切换次数本身是核心指标:频繁切换说明网络或主库有问题

集群

指标 来源 告警建议
cluster_state CLUSTER INFO 不是 ok 立即告警
cluster_slots_assigned / _ok / _pfail / _fail 同上 必须是 16384 / 16384 / 0 / 0
cluster_known_nodes / cluster_size 同上 少于预期说明有节点掉队
各节点的槽数与内存 CLUSTER NODES 分布不均是隐性问题:槽数不多但内存高 → 大 key 或热点
MOVED / ASK 计数 客户端埋点 稳定期大量 MOVED 说明路由表在抖

三、故障手册:症状 → 根因 → 第一步动作

症状 最可能的根因 第一步动作
从库读到旧值 ① 复制延迟;② 从库被慢查询阻塞;③ 从库正在加载 RDB 比 offset 差;查从库 SLOWLOG;查 master_sync_in_progress
写报 READONLY 连到了从库:切换后客户端没刷新,或配置写反 查该节点 role;强制客户端重新发现主库(第 03 章)
复制反复全量重传 backlog 太小,或从库长期落后 看 repl_backlog_histlen 是否打满;按第 01 章公式调大
主库 CPU 高但 QPS 不高 从库过多导致复制输出放大 / 大 key / 慢命令 查 SLOWLOG 与 connected_slaves
切换后任务/写入丢失 异步复制的固有窗口 确认丢失范围;关键写入加 WAIT;队列类数据改独立实例(第 04 章)
集群 cluster_state:fail 有槽无人认领(主节点挂且无副本) CLUSTER NODES 找 fail 的槽;补副本
用户看到 MOVED 报错 客户端不支持集群协议 换支持集群的客户端(第 05 章)
报 CROSSSLOT 多键 / Lua / 事务跨槽 加 hash tag,或拆成多次单键操作
迁移后部分 key 读不到 迁移中断,槽停在中间态 CLUSTER SETSLOT ... STABLE 或 --cluster fix,再核对 key 归属
单个 key 访问极慢 热点 key(分片解决不了) 本地缓存 / 拆分 key / 限流

排障路径收敛成一张图:

flowchart TD
    A["Redis 相关报警"] --> B{"是连通性问题<br/>还是正确性问题?"}
    B -- 连通性 --> C{"集群还是主从?"}
    C -- 主从 --> C1["看 master_link_status<br/>与哨兵 flags"]
    C -- 集群 --> C2["看 cluster_state<br/>与 cluster_slots_fail"]
    C1 --> C3{"正在切换?"}
    C3 -- 是 --> C4["等重试生效<br/>(第 03 章)"]
    C3 -- 否 --> C5["查进程/网络/连接数"]
    C2 --> C6{"槽有缺失?"}
    C6 -- 是 --> C7["补副本或重新认领槽"]
    C6 -- 否 --> C8["查节点存活与总线端口"]
    B -- 正确性 --> D{"读到旧值<br/>还是读到缺失?"}
    D -- 旧值 --> D1["比 offset 差<br/>确认该读是否必须走主"]
    D -- 缺失 --> D2{"刚发生切换或迁移?"}
    D2 -- 是 --> D3["异步复制丢数据(04章)<br/>或迁移中断(06章)"]
    D2 -- 否 --> D4["查是否写到了别的节点<br/>(MOVED/CROSSSLOT 被吞)"]
    B -- 慢 --> E["查 SLOWLOG + 客户端侧 P99<br/>区分服务端慢与网络慢"]

四、四类角色怎么选形态(本课的应用落地)

回到第 00 章那张表。核心建议先说:按角色拆实例,不要让"要安全的数据"替"要吞吐的数据"做妥协。

缓存(article:{1001}:detail)

队列(ARQ)

锁(lock:index:{1001})

事件流(Stream + 消费者组)

五、客户端侧的落地清单

上任何形态之前,客户端这一层必须过关:

  1. 读写分流有显式开关(allow_stale),而不是埋在配置里;
  2. 写失败有重试,重试覆盖切换窗口(第 03 章 safe_write);
  3. 从库失败能回退主库,且回退有打点;
  4. 连接池按角色分开(读池 / 写池),避免一个池打满影响另一路;
  5. 超时设置合理:连接超时 < 命令超时 < 业务超时,否则重试会叠加成雪崩;
  6. 多键与 Lua 已按 hash tag 改造(若上集群);
  7. 客户端库支持对应形态:哨兵客户端 / 集群客户端,并验证过切换与重定向行为。

六、上线检查清单

检查项 通过标准
复制关系写进配置文件 只用命令配的关系,重启后仍在(不是"重启就没了")
backlog 按写入速率调过 不是默认 1MB(写密集实例)
哨兵数量与部署 ≥3 个、奇数、跨机器、与数据节点分开
客户端重试演练 切换时持续写入,失败后重试成功
从库宕机演练 读仍成功、回退有打点、恢复后回到从库
集群槽覆盖 --cluster check 通过,16384 全覆盖
集群副本 每个主节点至少 1 个副本,且与主不同机
监控与告警 第 02 节的拓扑指标已采且有阈值
对账机制 能回答"到底有没有丢数据"

七、练习与验收

练习 1:按第 02 节的表,把你环境的实际取值填一遍,指出哪些指标目前没采集。

练习 2:从第 06 节的四类角色里挑一类,为它写一份"形态选择说明":为什么选这个形态、代码上要改什么、出问题怎么降级。

练习 3:按第 03 节的故障手册,给自己系统里最近的一次 Redis 故障写一份复盘:症状属于哪一类、当时的第一步动作对不对。

验收点:不看资料,说出三套形态各自的关键监控项;对"读到旧值""写报 READONLY""切换后丢数据""CROSSSLOT"四种症状,立刻给出第一步排查动作;说明四类角色各自的形态选择与理由。


全课收口:回到第 00 章的决策图。现在你应该能不看资料画出它,并对每条分支说出"为什么"——瓶颈是什么、这套形态解决什么、代价是什么、应用代码要改什么。三句话总结全课:

  1. 复制让数据多一份,但它是异步的,且不提供路由;
  2. 哨兵解决主库单点,不解决容量,也不保证写入安全;
  3. 集群解决容量,代价是跨槽能力受限——而真正的难点从来不是搭集群,是 key 设计。

进入 keel 阅读