Redis 学习笔记(第四期):高可用与集群(哨兵 + Cluster + 容器化)
本期目标:系统讲解 Redis 生产环境高可用与分布式方案,包括哨兵模式、Redis Cluster 集群、Docker 容器化部署、监控告警、多语言客户端、安全加固及容量规划。全文所有示例均可在实际环境中复现。
目录
- 高可用与集群的必要性
- 哨兵模式(Sentinel)
2.1 架构与核心概念
2.2 主观下线与客观下线
2.3 故障转移完整流程(含 Raft 选举)
2.4 一主两从 + 三哨兵搭建(详细步骤)
2.5 故障模拟与恢复验证
2.6 脑裂预防与min-replicas-to-write
2.7 哨兵常见问题与调优
2.8 哨兵配置参数大全 - Redis Cluster 集群
3.1 集群 vs 哨兵:选型对比
3.2 哈希槽与数据分布(含 Hash Tag)
3.3 节点通信与 Gossip 协议
3.4 3 主 3 从集群搭建(6 节点)
3.5 集群读写与 MOVED/ASK 重定向
3.6 集群故障转移演示
3.7 集群扩缩容:添加节点、槽迁移、删除节点
3.8 集群生产配置优化与常见坑
3.9 集群跨槽操作限制及解决方案 - Docker 容器化部署 Redis
4.1 单机 Redis 容器(持久化 + 密码)
4.2 Docker Compose 部署 3 主 3 从集群
4.3 网络模式与cluster-announce-ip详解 - 监控与性能分析
5.1 关键监控指标(INFO 命令详解)
5.2 Prometheus + Grafana 集成(redis_exporter)
5.3 慢查询日志(SLOWLOG)
5.4 大 key 检测与热 key 分析
5.5 延迟诊断与 LATENCY 命令 - 多语言客户端连接示例
6.1 Python(redis-py,哨兵 + 集群)
6.2 Java(Jedis + Lettuce)
6.3 Go(go-redis)
6.4 Node.js(ioredis) - 安全最佳实践
7.1 ACL 用户管理(Redis 6+)
7.2 危险命令禁用与重命名
7.3 TLS 加密连接
7.4 网络隔离与最小权限 - 生产容量规划与压测
8.1 内存与节点数量估算公式
8.2 redis-benchmark 压测与结果解读
8.3 读写分离与客户端负载均衡策略 - 常见问题排查表(速查)
- 知识点总结表
1. 高可用与集群的必要性
Redis 作为内存数据库,单机存在诸多瓶颈:
| 场景 | 单机 Redis 问题 | 解决方案 |
|---|---|---|
| 主库宕机 | 服务不可用,需人工干预 | 哨兵模式:自动故障转移,秒级切换 |
| 内存不足 | 单机内存有限(推荐 ≤ 32GB) | Redis Cluster:数据分片,水平扩展 |
| 写入瓶颈 | 单主节点无法承载高并发写(≥5 万 QPS) | Cluster 多主:写请求分散到多个主节点 |
| 读瓶颈 | 主从读写分离仍受限于单主复制延迟 | Cluster + 从库:多主多从,读负载均衡 |
| 在线扩容 | 需停机或手动迁移数据 | Cluster 槽迁移:在线平滑扩容,业务无感知 |
2. 哨兵模式(Sentinel)
2.1 架构与核心概念
Redis Sentinel 是一个分布式系统,由多个 Sentinel 进程组成,用于监控 Redis 主从节点并在主库故障时自动提升从库。
核心组件:
- Master:主节点,处理写请求。
- Replica(Slave):从节点,复制主节点数据。
- Sentinel :监控节点,通常部署 3 个或 5 个(奇数),通过投票达成共识。
通信机制:
- Sentinel 之间通过 Gossip 协议交换状态信息。
- Sentinel 通过 Pub/Sub 频道
__sentinel__:hello自动发现其他 Sentinel。 - Sentinel 向 Redis 节点发送
INFO命令获取其从节点列表(每 10 秒一次)。
2.2 主观下线(SDOWN)与客观下线(ODOWN)
| 状态 | 含义 | 触发条件 |
|---|---|---|
| SDOWN(主观下线) | 单个 Sentinel 认为主库不可达 | 超过 down-after-milliseconds 毫秒未收到 PONG 响应 |
| ODOWN(客观下线) | 达到 quorum 数量的 Sentinel 都认为主库 SDOWN |
Sentinel 间通过 SENTINEL is-master-down-by-addr 确认 |
quorum :配置
sentinel monitor mymaster 192.168.108.10 6379 2,其中 2 表示至少需要 2 个 Sentinel 同意才标记 ODOWN(3 个哨兵时 quorum=2 是常见设置)。
2.3 故障转移完整流程(基于 Raft 选举)
- SDOWN 检测 (0~30 秒):每个 Sentinel 每秒 PING 主库,若
down-after-milliseconds内无响应,标记 SDOWN。 - ODOWN 确认(<1 秒):Sentinel 向其他 Sentinel 询问,若达到 quorum,标记 ODOWN。
- 选举 Leader Sentinel(<1 秒):所有 Sentinel 通过 Raft 算法选举一个 Leader(先到先得,获得多数票)。每个 Sentinel 只能投一票,保证只有一个 Leader。
- 选择新主库 (<1 秒):Leader 从从库中选出最优者,优先级规则(按顺序):
- 筛选 :排除与主库失联的、排除有
S_DOWN或O_DOWN的。 - 优先级 :
slave-priority(replica-priority)值越小越优先(默认 100)。 - 同步偏移量 :复制偏移量(
offset)越大越优先(数据最新)。 - 运行 ID:按 runid 字典序最小。
- 筛选 :排除与主库失联的、排除有
- 执行切换 (1~2 秒):
- 对新主库执行
REPLICAOF NO ONE。 - 让其他从库指向新主库(
REPLICAOF new_master_ip port)。 - 更新旧主库配置(若恢复)为从库。
- 对新主库执行
- 通知客户端 :Sentinel 通过
+switch-master频道发布主库变更,客户端可订阅。
总切换时间 :通常 10~30 秒 (主要取决于 down-after-milliseconds 设置,建议 10~30 秒)。
2.4 一主两从 + 三哨兵搭建(详细步骤)
环境准备(三台机器,假设 IP 为 10, 11, 12)
| 节点 | IP | Redis 角色 | Sentinel 端口 | 数据目录 |
|---|---|---|---|---|
| node1 | 192.168.108.10 | Master | 26379 | /var/lib/redis |
| node2 | 192.168.108.11 | Slave | 26379 | /var/lib/redis |
| node3 | 192.168.108.12 | Slave | 26379 | /var/lib/redis |
前置条件:
- 已在三台机器上配置好 Redis 主从复制(第三期内容),并设置
requirepass master123,masterauth master123。 - 关闭防火墙或开放 6379、26379 端口。
- 时间同步(
ntpdate ntp.aliyun.com)。
步骤 1:创建 Sentinel 配置文件(三台机器内容相同)
编辑 /etc/redis-sentinel.conf(可从 /usr/local/redis-6.2.14/sentinel.conf 复制修改):
conf
bind 0.0.0.0
port 26379
daemonize yes
pidfile /var/run/redis-sentinel.pid
logfile /var/log/redis/sentinel.log
dir /var/lib/redis
# 监控主库:名称 mymaster,IP 192.168.108.10,端口 6379,quorum=2
sentinel monitor mymaster 192.168.108.10 6379 2
# 主库密码
sentinel auth-pass mymaster master123
# 主观下线时间(毫秒),默认 30000
sentinel down-after-milliseconds mymaster 30000
# 故障转移超时时间(毫秒),默认 180000
sentinel failover-timeout mymaster 180000
# 故障转移时最多同时同步多少个从库(防止主库网络拥塞)
sentinel parallel-syncs mymaster 1
# 可选:通知脚本(例如发送邮件或钉钉)
# sentinel notification-script mymaster /usr/local/bin/redis_failover_notify.sh
步骤 2:启动 Sentinel
bash
redis-sentinel /etc/redis-sentinel.conf
# 或使用 redis-server 带 --sentinel 参数
redis-server /etc/redis-sentinel.conf --sentinel
确认启动成功:
bash
ps aux | grep sentinel
tail -f /var/log/redis/sentinel.log # 应看到 +monitor 等信息
步骤 3:验证哨兵状态
连接任意 Sentinel:
bash
redis-cli -h 192.168.108.10 -p 26379
常用命令:
bash
# 查看所有监视的主库
127.0.0.1:26379> SENTINEL masters
1) 1) "name"
2) "mymaster"
3) "ip"
4) "192.168.108.10"
5) "port"
6) "6379"
7) "flags"
8) "master"
...
# 查看从库列表
127.0.0.1:26379> SENTINEL slaves mymaster
1) 1) "name"
2) "192.168.108.11:6379"
...
2) 1) "name"
2) "192.168.108.12:6379"
...
# 查看哨兵集群信息
127.0.0.1:26379> INFO sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
master0:name=mymaster,status=ok,address=192.168.108.10:6379,slaves=2,sentinels=3
2.5 故障模拟与恢复验证
步骤 1:在 node1 上停止主库
bash
redis-cli -h 192.168.108.10 -a master123 SHUTDOWN
步骤 2:观察哨兵日志(在任意 Sentinel 节点)
bash
tail -f /var/log/redis/sentinel.log
关键日志输出(约 30 秒后):
text
[2024-01-15 10:00:05] +sdown master mymaster 192.168.108.10 6379
[2024-01-15 10:00:06] +odown master mymaster 192.168.108.10 6379 #quorum 2/2
[2024-01-15 10:00:06] +try-failover master mymaster
[2024-01-15 10:00:06] +vote-for-leader <sentinel-id> 1
[2024-01-15 10:00:06] +failover-state-select-slave master mymaster
[2024-01-15 10:00:06] +selected-slave slave 192.168.108.11:6379
[2024-01-15 10:00:06] +failover-state-send-slaveof-noone slave 192.168.108.11:6379
[2024-01-15 10:00:06] +failover-state-wait-promotion slave 192.168.108.11:6379
[2024-01-15 10:00:07] +promoted-slave slave 192.168.108.11:6379
[2024-01-15 10:00:07] +failover-state-reconf-slaves master mymaster
[2024-01-15 10:00:07] +slave-reconf-sent slave 192.168.108.12:6379
[2024-01-15 10:00:08] +slave-reconf-done slave 192.168.108.12:6379
[2024-01-15 10:00:08] +failover-end master mymaster
[2024-01-15 10:00:08] +switch-master mymaster 192.168.108.10 6379 192.168.108.11 6379
步骤 3:验证新主库
bash
redis-cli -h 192.168.108.11 -a master123 INFO replication
role:master
connected_slaves:1
slave0:ip=192.168.108.12,port=6379
步骤 4:恢复原主库(启动后自动成为从库)
bash
systemctl start redis # 或 redis-server
redis-cli -h 192.168.108.10 -a master123 INFO replication
role:slave
master_host:192.168.108.11
master_port:6379
2.6 脑裂(Split-brain)预防与配置
问题描述:网络分区导致旧主库与 Sentinel 失联,但旧主库仍接受写入。当网络恢复后,旧主库被降级为从库,期间写入的数据丢失。
解决方案 :配置 min-replicas-to-write 和 min-replicas-max-lag,要求主库写入前至少有一个从库已同步且延迟在阈值内。
在主库配置文件或动态设置:
bash
redis-cli -h 192.168.108.10 -a master123 CONFIG SET min-replicas-to-write 1
redis-cli -h 192.168.108.10 -a master123 CONFIG SET min-replicas-max-lag 10
效果 :如果活跃从库数量 < 1 或最大延迟 > 10 秒,主库会拒绝写请求,返回 NOREPLICAS 错误。这确保了数据至少被一个从库确认,避免脑裂。
2.7 哨兵常见问题与调优
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 频繁误切换 | down-after-milliseconds 过小,网络波动 |
调大至 30000 以上,或提高 quorum |
| 切换时间过长 | down-after-milliseconds 太大 |
权衡稳定性与切换速度,设为 10000~20000 |
| 哨兵与 Redis 同机故障 | 主机宕机导致两者同时不可用 | 分离部署(不同物理机 / 容器) |
| 客户端连接哨兵超时 | 哨兵地址列表不全或网络问题 | 配置多个哨兵地址,使用连接池 |
Sentinel 日志出现 tilt 模式 |
系统时间跳跃或高负载 | 检查服务器时间和负载,适当增大 sentinel 的 failover-timeout |
动态修改哨兵配置(无需重启):
bash
redis-cli -p 26379 SENTINEL SET mymaster down-after-milliseconds 15000
2.8 哨兵配置参数大全
| 参数 | 示例 | 说明 |
|---|---|---|
sentinel monitor |
sentinel monitor mymaster 192.168.108.10 6379 2 |
监控主库,quorum=2 |
sentinel auth-pass |
sentinel auth-pass mymaster master123 |
主库密码 |
sentinel down-after-milliseconds |
sentinel down-after-milliseconds mymaster 30000 |
主观下线超时(毫秒) |
sentinel failover-timeout |
sentinel failover-timeout mymaster 180000 |
故障转移总超时(毫秒) |
sentinel parallel-syncs |
sentinel parallel-syncs mymaster 1 |
同时同步的从库数 |
sentinel notification-script |
sentinel notification-script mymaster /path/to/script |
切换时的通知脚本 |
sentinel client-reconfig-script |
sentinel client-reconfig-script mymaster /path/to/script |
客户端重新配置脚本 |
3. Redis Cluster 集群
3.1 集群 vs 哨兵:选型对比
| 维度 | 哨兵 + 主从 | Redis Cluster |
|---|---|---|
| 数据量 | 受限于单机内存(推荐 ≤ 32GB) | 水平扩展,容量可达 TB 级 |
| 写并发 | 单主,受限于单核 | 多主并行,近似线性提升 |
| 自动故障转移 | ✅ | ✅ |
| 多键操作 | 全支持(如交集、并集) | 仅支持同一槽的 key(需 hash tag) |
| 运维复杂度 | 较低(无需管理槽位) | 中等(需管理槽位、节点) |
| 客户端要求 | 任何 Redis 客户端 | 需要支持 Cluster 协议(如 -c 参数或智能客户端) |
| 在线扩容 | 需手动迁移数据(停机或复杂脚本) | 在线槽迁移,平滑扩展 |
选型建议:
- 数据量 < 100GB,QPS < 5 万,多键操作频繁 → 哨兵。
- 数据量 > 200GB,高并发写,可接受单槽限制 → Cluster。
3.2 哈希槽(Hash Slot)与数据分布
- 固定槽数量:16384 个(0 ~ 16383)。
- 键到槽的映射 :
slot = CRC16(key) & 16383(CRC16 返回 16 位,取低 14 位)。 - Hash Tag :若键包含
{...},只对花括号内的内容计算哈希。例如user:{1001}:name和user:{1001}:age落在同一槽,支持多键操作。
为什么是 16384?
- 网络心跳消息大小:16384 个槽的位图仅 2KB,适合 Gossip 传播。
- 节点数量上限:官方建议集群节点数不超过 1000,16384 足够分配。
槽分配示例(3 个主节点):
- 主节点 A:槽 0 ~ 5460(5461 个槽)
- 主节点 B:槽 5461 ~ 10922(5462 个槽)
- 主节点 C:槽 10923 ~ 16383(5461 个槽)
3.3 节点通信(Gossip)与集群总线
- 每个节点监听两个端口:
- 客户端端口:默认 6379。
- 集群总线端口 :默认 16379(
客户端端口 + 10000),用于节点间通信。
- 消息类型 :
PING(检测存活)、PONG(响应)、MEET(邀请新节点)、FAIL(广播节点故障)、UPDATE(槽映射更新)。 - Gossip 协议:节点间定期交换信息,最终一致,不依赖中心节点。
配置相关:
conf
cluster-node-timeout 15000 # 节点超时时间(毫秒)
cluster-announce-port 6379 # 客户端端口(用于 NAT 环境)
cluster-announce-bus-port 16379 # 总线端口
3.4 3 主 3 从集群搭建(6 节点)
环境规划(6 台虚拟机,或使用 6 个不同端口,此处以 6 台为例)
| 节点 | IP | 角色 |
|---|---|---|
| redis1 | 192.168.108.21 | master1 |
| redis2 | 192.168.108.22 | master2 |
| redis3 | 192.168.108.23 | master3 |
| redis4 | 192.168.108.24 | slave1(复制 master1) |
| redis5 | 192.168.108.25 | slave2(复制 master2) |
| redis6 | 192.168.108.26 | slave3(复制 master3) |
步骤 1:安装 Redis(所有节点,版本 ≥5.0)
bash
yum install -y gcc gcc-c++ tcl make
cd /usr/local
tar zxvf redis-6.2.14.tar.gz
cd redis-6.2.14
make && make install
步骤 2:配置 redis.conf(所有节点,基本一致)
创建 /usr/local/redis-6.2.14/redis.conf:
conf
bind 0.0.0.0
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "/var/log/redis/redis.log"
dir /data/redis
# 集群关键配置
cluster-enabled yes
cluster-config-file nodes.conf # 自动生成,勿手动编辑
cluster-node-timeout 15000
# 持久化(强烈推荐)
appendonly yes
appendfsync everysec
# 密码(所有节点必须一致)
requirepass cluster123
masterauth cluster123
创建数据目录并启动:
bash
mkdir -p /data/redis
chown -R redis:redis /data/redis # 如果使用 redis 用户
redis-server /usr/local/redis-6.2.14/redis.conf
步骤 3:创建集群(在任意节点执行)
bash
cd /usr/local/redis-6.2.14/
redis-cli --cluster create \
192.168.108.21:6379 \
192.168.108.22:6379 \
192.168.108.23:6379 \
192.168.108.24:6379 \
192.168.108.25:6379 \
192.168.108.26:6379 \
--cluster-replicas 1 \
-a cluster123
参数 :--cluster-replicas 1 表示每个主节点配一个从节点。前 3 个 IP 成为主,后 3 个成为从。
执行输出 (需输入 yes):
text
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460 (5461 slots)
Master[1] -> Slots 5461 - 10922 (5462 slots)
Master[2] -> Slots 10923 - 16383 (5461 slots)
Adding replica 192.168.108.24:6379 to 192.168.108.21:6379
Adding replica 192.168.108.25:6379 to 192.168.108.22:6379
Adding replica 192.168.108.26:6379 to 192.168.108.23:6379
>>> Nodes configuration updated
>>> Sending CLUSTER MEET messages to join the cluster
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
步骤 4:验证集群状态
bash
redis-cli -c -h 192.168.108.21 -p 6379 -a cluster123
集群信息:
bash
127.0.0.1:6379> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:3
cluster_my_epoch:1
节点列表:
bash
127.0.0.1:6379> CLUSTER NODES
fcb0f043... 192.168.108.21:6379@16379 master - 0 1734220800 1 connected 0-5460
f3cb00b0... 192.168.108.22:6379@16379 master - 0 1734220800 2 connected 5461-10922
90507dea... 192.168.108.23:6379@16379 master - 0 1734220800 3 connected 10923-16383
18b45e54... 192.168.108.24:6379@16379 slave fcb0f043... 0 1734220800 1 connected
...
3.5 集群读写与 MOVED/ASK 重定向
bash
# 使用 -c 参数启用集群模式(自动跟随重定向)
redis-cli -c -h 192.168.108.21 -p 6379 -a cluster123
127.0.0.1:6379> SET user:1001 "Alice"
-> Redirected to slot [5798] located at 192.168.108.22:6379
OK
127.0.0.1:6379> GET user:1001
"Alice"
127.0.0.1:6379> CLUSTER KEYSLOT user:1001
(integer) 5798
MOVED 错误(非集群模式) :
如果不加 -c,会返回 MOVED 错误,客户端需自行重定向。
ASK 重定向 :槽迁移过程中,临时重定向,客户端需先发送 ASKING 命令再执行操作。
3.6 集群故障转移演示
关闭 master1 (192.168.108.21):
bash
redis-cli -h 192.168.108.21 -a cluster123 SHUTDOWN
观察集群状态(在另一节点):
bash
redis-cli -h 192.168.108.22 -a cluster123 CLUSTER NODES
输出显示:
text
fcb0f043... 192.168.108.21:6379@16379 master,fail - ...
18b45e54... 192.168.108.24:6379@16379 master - 0 1734220900 7 connected 0-5460
原从节点 (24) 已晋升为新主库,负责槽 0-5460。
验证写入:
bash
redis-cli -c -h 192.168.108.24 -p 6379 -a cluster123 SET user:1002 "Bob"
OK
恢复原主库(启动后自动成为从库):
bash
redis-server /usr/local/redis-6.2.14/redis.conf
# 稍等后查看其角色
redis-cli -h 192.168.108.21 -a cluster123 CLUSTER NODES
# 应显示为 slave
3.7 集群扩缩容:添加节点、槽迁移、删除节点
添加新主节点(redis7: 192.168.108.27)
启动新节点(配置同其他节点),然后加入集群:
bash
redis-cli --cluster add-node 192.168.108.27:6379 192.168.108.21:6379 -a cluster123
新节点目前没有槽位,状态为 master 但不负责槽。
迁移槽位到新节点(reshard)
bash
redis-cli --cluster reshard 192.168.108.21:6379 -a cluster123
交互式输入(示例):
- 要迁移的槽数量:
1000 - 目标节点 ID:
<redis7 的节点 ID> - 源节点 ID:
all(从所有现有主节点平均抽取)
迁移过程中集群仍可服务,可能遇到 ASK 重定向。可通过 CLUSTER NODES 监控迁移进度。
添加从节点
bash
redis-cli --cluster add-node 192.168.108.28:6379 192.168.108.21:6379 \
--cluster-slave --cluster-master-id <主节点ID> -a cluster123
删除节点
必须先迁移走节点上的所有槽(若为主节点),再删除:
bash
# 迁移槽位到其他节点(reshard,选择目标节点,源节点为待删除节点ID)
# 然后删除空节点
redis-cli --cluster del-node 192.168.108.21:6379 <node-id> -a cluster123
3.8 集群生产配置优化与常见坑
优化配置:
conf
# 内存与淘汰策略(每个主节点建议 ≤ 16GB)
maxmemory 16gb
maxmemory-policy allkeys-lru
# AOF 优化(减少磁盘 IO 阻塞)
appendonly yes
appendfsync everysec
no-appendfsync-on-rewrite yes # 重写时不 fsync
# 集群超时
cluster-node-timeout 15000
# 复制缓冲区(防止从库短时间断开后触发全量同步)
repl-backlog-size 128mb
# 客户端输出缓冲区(防止从库复制积压)
client-output-buffer-limit replica 256mb 64mb 60
常见坑:
- 最大内存设置不当:集群每个主节点内存应小于物理内存的 70%,否则 fork 耗时过长。
- 密码不一致 :所有节点的
requirepass和masterauth必须相同,否则节点间无法通信。 - 网络时间协议(NTP)不同步:集群节点时间差过大会导致误判故障,务必同步时间。
- 槽未全覆盖 :集群启动后必须检查
cluster_slots_ok是否为 16384。
3.9 集群跨槽操作限制及解决方案
问题 :MSET、SUNION 等涉及多个 key 的操作,若 key 不在同一槽,会返回 CROSSSLOT 错误。
解决方案:
- 使用 Hash Tag :将相关 key 的
{}部分设为相同值,如user:{1001}:name和user:{1001}:age。 - 拆分命令:客户端拆分多次请求,或使用 Lua 脚本(脚本内也需遵守跨槽限制)。
示例:
bash
# 错误写法(跨槽)
MSET a 1 b 2 # 可能不同槽
# 正确写法(使用 hash tag)
MSET user:{1001}:name Alice user:{1001}:age 25 # 同一槽
4. Docker 容器化部署 Redis
4.1 单机 Redis 容器(持久化 + 密码)
bash
docker pull redis:7.2
docker run -d --name redis \
-p 6379:6379 \
-v /data/redis:/data \
redis:7.2 redis-server --appendonly yes --requirepass mypass
测试:
bash
docker exec redis redis-cli -a mypass SET hello world
docker exec redis redis-cli -a mypass GET hello
4.2 Docker Compose 部署 3 主 3 从集群
创建 docker-compose-cluster.yml(完整版,约 120 行,以下展示关键部分):
yaml
version: '3.8'
services:
redis1:
image: redis:7.2
command: redis-server --cluster-enabled yes --appendonly yes --requirepass cluster123 --masterauth cluster123
ports:
- "6379:6379"
- "16379:16379"
volumes:
- ./data/redis1:/data
redis2:
image: redis:7.2
command: redis-server --cluster-enabled yes --appendonly yes --requirepass cluster123 --masterauth cluster123
ports:
- "6380:6379"
- "16380:16379"
volumes:
- ./data/redis2:/data
# ... redis3 到 redis6 类似配置,端口递增
cluster-init:
image: redis:7.2
depends_on: [redis1, redis2, redis3, redis4, redis5, redis6]
command: >
sh -c "echo 'yes' | redis-cli --cluster create
redis1:6379 redis2:6379 redis3:6379
redis4:6379 redis5:6379 redis6:6379
--cluster-replicas 1 -a cluster123"
启动:
bash
docker-compose -f docker-compose-cluster.yml up -d
docker-compose logs cluster-init # 查看集群创建日志
4.3 网络模式与 cluster-announce-ip 详解
在 Docker 中,容器内 IP 可能与宿主机不同,导致 MOVED 重定向返回容器 IP,外部客户端无法访问。解决方案:
- 使用 host 网络模式 :
--net=host,容器直接使用宿主机 IP。 - 或配置
cluster-announce-ip:在redis.conf或命令行中指定宿主机 IP。
conf
cluster-announce-ip 192.168.108.21 # 宿主机 IP
cluster-announce-port 6379
cluster-announce-bus-port 16379
5. 监控与性能分析
5.1 关键监控指标(INFO 命令详解)
| 指标 | 命令 | 健康阈值 | 说明 |
|---|---|---|---|
| 内存使用率 | INFO memory → used_memory / maxmemory |
< 70% | 过高可能导致 OOM |
| 命中率 | INFO stats → keyspace_hits / (keyspace_hits+keyspace_misses) |
> 90% | 命中率低说明缓存效果差 |
| 复制延迟 | INFO replication → lag |
< 5 秒 | 从库延迟 |
| 集群状态 | CLUSTER INFO → cluster_state |
ok |
故障时变为 fail |
| 慢查询数 | SLOWLOG LEN |
酌情 | 过多说明存在慢命令 |
| 连接数 | INFO clients → connected_clients |
< 5000 | 超出 maxclients 会拒绝连接 |
5.2 Prometheus + Grafana 集成(redis_exporter)
bash
docker run -d --name redis_exporter -p 9121:9121 oliver006/redis_exporter \
--redis.addr redis://192.168.108.21:6379 --redis.password cluster123
配置 Prometheus 抓取 localhost:9121,Grafana 导入 Redis dashboard(官方 ID 763)。
5.3 慢查询日志(SLOWLOG)
配置阈值(微秒):
bash
redis-cli CONFIG SET slowlog-log-slower-than 10000 # 10 毫秒
redis-cli CONFIG SET slowlog-max-len 128
查看慢查询:
bash
redis-cli SLOWLOG GET 10
输出示例:
text
1) 1) (integer) 42
2) (integer) 1734220800
3) (integer) 15000
4) 1) "KEYS"
2) "*"
5) "127.0.0.1:45678"
5.4 大 key 检测与热 key 分析
大 key(扫描所有 key,可能阻塞,生产慎用):
bash
redis-cli -a cluster123 --bigkeys
热 key (需淘汰策略为 LFU,maxmemory-policy allkeys-lfu):
bash
redis-cli -a cluster123 --hotkeys
5.5 延迟诊断与 LATENCY 命令
bash
# 查看内置延迟监控(需开启 latency-monitor-threshold)
redis-cli CONFIG SET latency-monitor-threshold 100 # 100 毫秒
# 查看延迟日志
redis-cli LATENCY LATEST
redis-cli LATENCY HISTORY command
6. 多语言客户端连接示例
6.1 Python(redis-py)
哨兵模式:
python
from redis.sentinel import Sentinel
sentinel = Sentinel([
('192.168.108.10', 26379),
('192.168.108.11', 26379),
('192.168.108.12', 26379)
], socket_timeout=0.1)
master = sentinel.master_for('mymaster', password='master123')
master.set('foo', 'bar')
slave = sentinel.slave_for('mymaster', password='master123')
print(slave.get('foo')) # b'bar'
集群模式:
python
from redis.cluster import RedisCluster
rc = RedisCluster(
host='192.168.108.21',
port=6379,
password='cluster123',
decode_responses=True
)
rc.set('foo', 'bar')
print(rc.get('foo'))
6.2 Java(Jedis + Lettuce)
Jedis 哨兵:
java
JedisSentinelPool pool = new JedisSentinelPool("mymaster",
Sets.newHashSet("192.168.108.10:26379", "192.168.108.11:26379"),
"master123");
try (Jedis jedis = pool.getResource()) {
jedis.set("k", "v");
}
Lettuce 集群:
java
RedisClusterClient client = RedisClusterClient.create(
"redis://:cluster123@192.168.108.21:6379,192.168.108.22:6379"
);
StatefulRedisClusterConnection<String, String> conn = client.connect();
conn.sync().set("foo", "bar");
6.3 Go(go-redis)
哨兵:
go
import "github.com/go-redis/redis/v8"
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: "mymaster",
SentinelAddrs: []string{"192.168.108.10:26379", "192.168.108.11:26379"},
Password: "master123",
})
集群:
go
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{"192.168.108.21:6379", "192.168.108.22:6379"},
Password: "cluster123",
})
6.4 Node.js(ioredis)
哨兵:
javascript
const Redis = require('ioredis');
const redis = new Redis({
sentinels: [
{ host: '192.168.108.10', port: 26379 },
{ host: '192.168.108.11', port: 26379 }
],
name: 'mymaster',
password: 'master123'
});
集群:
javascript
const Redis = require('ioredis');
const cluster = new Redis.Cluster([
{ host: '192.168.108.21', port: 6379 },
{ host: '192.168.108.22', port: 6379 }
], { redisOptions: { password: 'cluster123' } });
7. 安全最佳实践
7.1 ACL 用户管理(Redis 6+)
创建只读用户:
bash
redis-cli -a cluster123 ACL SETUSER readonly on >readonlypass +@read +info
创建读写用户:
bash
redis-cli -a cluster123 ACL SETUSER readwrite on >readwritepass +@all -@dangerous
查看用户:
bash
redis-cli ACL LIST
7.2 危险命令禁用与重命名
在 redis.conf 中:
conf
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG "my_config_cmd"
rename-command KEYS ""
rename-command DEBUG ""
7.3 TLS 加密连接
生成自签名证书后配置:
conf
tls-port 6379
port 0
tls-cert-file /path/to/redis.crt
tls-key-file /path/to/redis.key
tls-ca-cert-file /path/to/ca.crt
客户端连接使用 rediss:// 协议。
7.4 网络隔离与最小权限
- 使用防火墙限制 6379 端口仅允许业务服务器访问。
- 集群总线端口(16379)限制在集群节点之间。
- 不要以 root 用户运行 Redis(配置
user选项)。
8. 生产容量规划与压测
8.1 内存与节点数量估算公式
- 总数据量 = 业务预估值 × (1 + 增长率)
- 每主节点内存 ≤ 20GB(建议 16GB),避免 fork 耗时过长
- 主节点数量 = ceil(总数据量 / 16GB)
- 总节点数 = 主节点数 × (1 + 副本数) # 副本数通常为 1
示例:业务数据 200GB,每主 16GB → 13 个主节点,加 13 个从节点,共 26 节点。
8.2 redis-benchmark 压测与结果解读
bash
# 测试 set 性能(100 并发,10 万请求)
redis-benchmark -h 192.168.108.21 -p 6379 -a cluster123 -c 100 -n 100000 -t set -q
# 测试管道(Pipeline)
redis-benchmark -h 192.168.108.21 -p 6379 -a cluster123 -c 100 -n 100000 -t set -P 10 -q
输出解读:
text
SET: 85000.00 requests per second
若 QPS 低于预期,检查网络延迟、CPU 使用率、是否开启 AOF always 模式。
8.3 读写分离与客户端负载均衡策略
- 哨兵模式:客户端从 Sentinel 获取从库列表,轮询或随机选择。
- 集群模式 :从库默认不处理读请求,需执行
READONLY命令(但仅对当前连接有效)。推荐在从库上配置replica-read-only yes并使用专用连接读取。
9. 常见问题排查表(速查)
| 错误 | 原因 | 解决 |
|---|---|---|
(error) MOVED |
客户端未启用集群模式 | 使用 redis-cli -c 或智能客户端 |
(error) CLUSTERDOWN |
槽未全覆盖或节点宕机 | redis-cli --cluster fix |
(error) CROSSSLOT |
多键操作跨槽 | 使用 {} hash tag |
(error) NOREPLICAS |
min-replicas-to-write 未满足 |
检查从库状态,或降低配置 |
READONLY You can't write against a read only replica |
误向从库写入 | 检查客户端连接是否正确 |
| Sentinel 不切换 | quorum 不足或网络分区 | 检查 SENTINEL masters,调整 down-after-milliseconds |
| 复制延迟增大 | 从库慢查询或网络差 | SLOWLOG,增大 repl-backlog-size |
(error) ERR unknown command |
命令被重命名或禁用 | 检查 rename-command 配置 |
10. 知识点总结表
| 模块 | 核心概念 | 关键命令/配置 |
|---|---|---|
| 哨兵 | 监控、自动故障转移、quorum | sentinel monitor, sentinel down-after-milliseconds |
| 哨兵手动切换 | - | SENTINEL failover mymaster |
| Redis Cluster | 16384 槽,CRC16,去中心化 | cluster-enabled yes |
| 集群创建 | 3 主 3 从 | redis-cli --cluster create --cluster-replicas 1 |
| 集群扩缩容 | 添加节点、槽迁移 | add-node, reshard, del-node |
| 持久化 | RDB + AOF | appendonly yes, appendfsync everysec |
| 内存 | 淘汰策略 | maxmemory-policy allkeys-lru |
| 安全 | ACL、禁用命令 | ACL SETUSER, rename-command |
| 监控 | 指标采集 | redis_exporter, SLOWLOG, --bigkeys |
| 多语言客户端 | 哨兵、集群 | Python/Java/Go/Node.js 示例 |
| Docker | 容器化部署 | docker run, docker-compose |
结语:本期笔记是 Redis 系列的高可用与集群终极指南。建议读者在虚拟机或容器中亲手搭建哨兵和集群环境,并模拟故障转移、槽迁移等操作,以巩固理解。后续可继续学习 Redis 流(Streams)、Redis 模块(RediSearch、RedisJSON)以及 Kubernetes 上的 Redis Operator。感谢学习,期待您的反馈!