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 将服务端报告地址映射成客户端可访问地址。
优先修正网络和服务发现配置;地址映射是处理确实存在网络边界时的工具。
初学者建议
- 本地学习先使用单机 Redis。
- 真正有高可用要求时再学习 Sentinel。
- 数据或吞吐确实需要水平分片时再选择 Cluster。
- 不要用“以后可能扩展”作为立即引入 Cluster 的唯一理由。
- 高可用必须通过故障演练验证,不能只看连接成功。