Redis 学习笔记(第四期):高可用与集群(哨兵 + Cluster + 容器化)

Redis 学习笔记(第四期):高可用与集群(哨兵 + Cluster + 容器化)

本期目标:系统讲解 Redis 生产环境高可用与分布式方案,包括哨兵模式、Redis Cluster 集群、Docker 容器化部署、监控告警、多语言客户端、安全加固及容量规划。全文所有示例均可在实际环境中复现。


目录

  1. 高可用与集群的必要性
  2. 哨兵模式(Sentinel)
    2.1 架构与核心概念
    2.2 主观下线与客观下线
    2.3 故障转移完整流程(含 Raft 选举)
    2.4 一主两从 + 三哨兵搭建(详细步骤)
    2.5 故障模拟与恢复验证
    2.6 脑裂预防与 min-replicas-to-write
    2.7 哨兵常见问题与调优
    2.8 哨兵配置参数大全
  3. 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 集群跨槽操作限制及解决方案
  4. Docker 容器化部署 Redis
    4.1 单机 Redis 容器(持久化 + 密码)
    4.2 Docker Compose 部署 3 主 3 从集群
    4.3 网络模式与 cluster-announce-ip 详解
  5. 监控与性能分析
    5.1 关键监控指标(INFO 命令详解)
    5.2 Prometheus + Grafana 集成(redis_exporter)
    5.3 慢查询日志(SLOWLOG)
    5.4 大 key 检测与热 key 分析
    5.5 延迟诊断与 LATENCY 命令
  6. 多语言客户端连接示例
    6.1 Python(redis-py,哨兵 + 集群)
    6.2 Java(Jedis + Lettuce)
    6.3 Go(go-redis)
    6.4 Node.js(ioredis)
  7. 安全最佳实践
    7.1 ACL 用户管理(Redis 6+)
    7.2 危险命令禁用与重命名
    7.3 TLS 加密连接
    7.4 网络隔离与最小权限
  8. 生产容量规划与压测
    8.1 内存与节点数量估算公式
    8.2 redis-benchmark 压测与结果解读
    8.3 读写分离与客户端负载均衡策略
  9. 常见问题排查表(速查)
  10. 知识点总结表

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 选举)

  1. SDOWN 检测 (0~30 秒):每个 Sentinel 每秒 PING 主库,若 down-after-milliseconds 内无响应,标记 SDOWN。
  2. ODOWN 确认(<1 秒):Sentinel 向其他 Sentinel 询问,若达到 quorum,标记 ODOWN。
  3. 选举 Leader Sentinel(<1 秒):所有 Sentinel 通过 Raft 算法选举一个 Leader(先到先得,获得多数票)。每个 Sentinel 只能投一票,保证只有一个 Leader。
  4. 选择新主库 (<1 秒):Leader 从从库中选出最优者,优先级规则(按顺序):
    • 筛选 :排除与主库失联的、排除有 S_DOWNO_DOWN 的。
    • 优先级slave-priorityreplica-priority)值越小越优先(默认 100)。
    • 同步偏移量 :复制偏移量(offset)越大越优先(数据最新)。
    • 运行 ID:按 runid 字典序最小。
  5. 执行切换 (1~2 秒):
    • 对新主库执行 REPLICAOF NO ONE
    • 让其他从库指向新主库(REPLICAOF new_master_ip port)。
    • 更新旧主库配置(若恢复)为从库。
  6. 通知客户端 :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 master123masterauth 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-writemin-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 模式 系统时间跳跃或高负载 检查服务器时间和负载,适当增大 sentinelfailover-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}:nameuser:{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

常见坑

  1. 最大内存设置不当:集群每个主节点内存应小于物理内存的 70%,否则 fork 耗时过长。
  2. 密码不一致 :所有节点的 requirepassmasterauth 必须相同,否则节点间无法通信。
  3. 网络时间协议(NTP)不同步:集群节点时间差过大会导致误判故障,务必同步时间。
  4. 槽未全覆盖 :集群启动后必须检查 cluster_slots_ok 是否为 16384。

3.9 集群跨槽操作限制及解决方案

问题MSETSUNION 等涉及多个 key 的操作,若 key 不在同一槽,会返回 CROSSSLOT 错误。

解决方案

  • 使用 Hash Tag :将相关 key 的 {} 部分设为相同值,如 user:{1001}:nameuser:{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 memoryused_memory / maxmemory < 70% 过高可能导致 OOM
命中率 INFO statskeyspace_hits / (keyspace_hits+keyspace_misses) > 90% 命中率低说明缓存效果差
复制延迟 INFO replicationlag < 5 秒 从库延迟
集群状态 CLUSTER INFOcluster_state ok 故障时变为 fail
慢查询数 SLOWLOG LEN 酌情 过多说明存在慢命令
连接数 INFO clientsconnected_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。感谢学习,期待您的反馈!

相关推荐
YM52e1 天前
鸿蒙PC ArkTS 声明合并问题深度解析与最佳实践
学习·华为·harmonyos·鸿蒙·鸿蒙系统
海兰1 天前
【实用程序】电商销售分析仪表盘 — 从零搭建一个AI参与的全栈数据洞察系统
人工智能·学习·算法
凡人叶枫1 天前
Effective C++ 条款28:避免使用 handles 指向对象内部
linux·服务器·开发语言·c++·嵌入式开发
AI帮小忙1 天前
Debian系linux操作系统里安装OpenClaw
linux·运维·debian
极创信息1 天前
Linux挖矿病毒深度清理实战教程,从进程隐藏、Rootkit驻留到彻底根除
java·大数据·linux·运维·安全·tomcat·健康医疗
努力成为AK大王1 天前
并发编程的核心挑战、优化方案与核心知识点总结
java·开发语言·数据库
ken22321 天前
在 Libreoffice Calc中输入自定义表情字符时,需要保存之后,才能正常显示
学习
zwenqiyu1 天前
P5283 [十二省联考 2019] 异或粽子题解
c++·学习·算法
编程圈子1 天前
电机驱动开发学习2. 直流无刷电机工作原理
驱动开发·学习