Sentinel 与 Cluster 入门

单机 Redis 容易理解,但存在单点故障和容量上限。Sentinel 与 Cluster 解决的问题不同。

Sentinel 解决高可用

Sentinel 监控主节点和副本,在主节点故障时协调故障转移。Node-Redis 的 Sentinel 客户端会发现当前主节点,并在拓扑变化后重新配置连接。

import { createSentinel } from "redis";

const sentinel = await createSentinel({
  name: "mymaster",
  sentinelRootNodes: [
    { host: "sentinel-1.example.com", port: 26379 },
    { host: "sentinel-2.example.com", port: 26379 },
    { host: "sentinel-3.example.com", port: 26379 },
  ],
  nodeClientOptions: {
    username: "app-user",
    password: "从环境变量或密钥服务读取的密码",
  },
})
  .on("error", (error: Error) => {
    console.error("Redis Sentinel error", error);
  });

await sentinel.connect();

官方建议在地址可能变化的 Kubernetes 或云环境中使用可重新解析的主机名作为种子,而不是固定 IP。

Sentinel 不等于数据分片。它主要解决主从故障转移和高可用。

Cluster 解决分片和扩展

Redis Cluster 将键分布到多个哈希槽,再由多个主节点负责不同槽位。

import { createCluster } from "redis";

const cluster = createCluster({
  rootNodes: [
    { url: "redis://redis-1.example.com:6379" },
    { url: "redis://redis-2.example.com:6379" },
    { url: "redis://redis-3.example.com:6379" },
  ],
  defaults: {
    username: "app-user",
    password: "从环境变量或密钥服务读取的密码",
  },
});

cluster.on("error", (error: Error) => {
  console.error("Redis Cluster error", error);
});

await cluster.connect();

不需要列出所有节点,根节点用于发现拓扑。官方文档指出,凭据、TLS、超时等要应用到所有发现节点的配置,应放在 defaults 中,而不是只放在 rootNodes

多键命令的槽位限制

Cluster 会根据键计算槽位。多键命令通常要求所有键在同一槽位:

order:{42}:header
order:{42}:items

花括号中的 {42} 是 Hash Tag,可让两个键进入同一槽位。

不要为了绕过限制让所有键都使用同一个 Hash Tag,那会把流量集中到一个分片,失去 Cluster 的意义。

Sentinel 与 Cluster 对比

问题 Sentinel Cluster
自动故障转移
数据分片
单个数据集容量突破单机内存
客户端路由复杂度 较低 较高
多键操作限制 接近单机 受槽位限制

读副本

Cluster 的 useReplicas: true 或 Sentinel 的副本池可以分担读命令。但副本复制通常是异步的,刚写入的数据可能暂时读不到。

需要“写后立刻读到”的业务,不应无条件把读取路由到副本。

地址映射

容器、NAT 或云环境中,Redis 返回的内部地址可能无法被应用访问。Node-Redis 提供 nodeAddressMap 将服务端报告地址映射成客户端可访问地址。

优先修正网络和服务发现配置;地址映射是处理确实存在网络边界时的工具。

初学者建议