1. 集群方案选型:为什么我最终选了Cluster模式
先交代一下背景。我之前维护的一套系统,Redis单节点扛了挺长时间,但随着业务量上来,问题开始暴露:首先是内存不够用,16G的实例光缓存数据就快撑爆了;其次是单点风险太明显,一次服务器重启,整个服务直接雪崩,所有请求穿透打到底层数据库,差点把库打挂。那次故障之后,我花了几个晚上研究Redis集群方案,最终落地了官方Cluster模式,并且完整地做了一轮功能验证。
如果你也想搭Redis集群,先把方案选型这一关想清楚。目前主流的Redis高可用方案就三种:主从复制、哨兵模式(Sentinel)、官方Cluster集群。很多人一上来就纠结“到底选哪个”,我的建议是看你的数据量和可用性要求。
主从复制是最基础的方案,一个主节点带着若干个从节点,数据单向同步。它能解决读压力,但主节点挂了之后,需要人工手动切换,而且切换期间服务是不可用的,从节点只会干等。对可用性要求稍高的场景,这方案基本不够看。
哨兵模式是在主从复制基础上加了Sentinel进程来监控主节点状态,主节点挂了能自动完成故障转移,把某个从节点提升为主节点。它能解决高可用问题,但它仍然是“一主多从”的架构,数据总量受限于单节点的内存上限。如果你有几百G甚至上T的数据要缓存,哨兵模式是扛不住的。
Cluster模式则是把数据分片存储。整个集群里有多个主节点,每个主节点负责一部分哈希槽(slot),总共16384个槽位,通过CRC16算法对key计算后映射到具体槽位。这样数据被拆散到了多台机器上,单机内存瓶颈被彻底打破,同时每个主节点还可以配置从节点做主备。Cluster模式既解决了容量扩展问题,又自带故障转移能力,这也是我最终选它的核心理由。
另外多提一句,现在云厂商卖的Redis通常也是基于Cluster思路做的,只是把运维细节封装好了。自己在物理机或云服务器上手动搭建,最大的意义在于搞清楚背后的原理,以后排查问题的时候能直接对应到具体环节。
2. 环境准备与集群规划:先把地基打牢
2.1 拓扑规划:6个节点是最小舒服配置
Redis Cluster官方要求至少3个主节点才能形成完整集群,为了高可用,每个主节点至少配1个从节点,所以6个节点(3主3从)是最常见的起步配置。
如果机器资源紧张,你甚至可以用6个端口在同一台机器上模拟,但生产环境我强烈不建议这样做——一台物理机挂了,所有节点一起挂,集群失去意义。我这次用的是3台服务器,每台上跑2个Redis实例,分别映射不同端口,既节省机器又保证基础的容错能力。
具体规划如下:
| 服务器 | 实例角色 | IP:端口 | 用途说明 |
|---|---|---|---|
| 服务器A | master | 192.168.1.101:6379 | 主节点1,负责部分槽位 |
| 服务器A | slave | 192.168.1.101:6380 | 主节点1的从节点,跨机冗余 |
| 服务器B | master | 192.168.1.102:6379 | 主节点2,负责部分槽位 |
| 服务器B | slave | 192.168.1.102:6380 | 主节点2的从节点,跨机冗余 |
| 服务器C | master | 192.168.1.103:6379 | 主节点3,负责部分槽位 |
| 服务器C | slave | 192.168.1.103:6380 | 主节点3的从节点,跨机冗余 |
这个拓扑的关键设计在于:同一台服务器的master和slave不能形成主备关系,比如A上的6379和A上的6380如果做成主备,等于是A这台机器挂了,两个节点一起失联。所以规划时我会让每个主节点的从节点落在另一台机器上,这样任意一台服务器宕机,该服务器上的主节点都能在其他机器找到对应的从节点继续顶上来。
2.2 版本选择:别用太老的,也别追最新
Redis版本这块我踩过坑。早期用过一个比较老的3.x版本,官方Cluster功能虽然存在,但坑不少,比如集群命令支持不够完整,在线扩缩容的体验也比较差。后面我统一升级到了6.2系列,稳定性和功能都算比较理想的折中。
6.x版本有几个值得关注的改进:一是多线程IO的引入,虽然默认关闭,但开启后对高并发场景有提升;二是集群相关的命令和客户端库支持已经非常成熟,主流语言的SDK都能直接对接Cluster模式;三是对ACL权限控制的支持,可以给不同应用分配不同权限。当然,如果你是新项目,直接用7.x也完全没问题,我这边因为涉及业务兼容性,没有激进升级。
下载方式推荐去Redis官网拿源码包编译安装,而不是图省事直接apt/yum安装。系统自带的版本往往偏旧,且不一定能拿到最新的稳定特性。用源码编译其实也不复杂,就几步命令的事:
# 下载并解压 wget https://download.redis.io/releases/redis-6.2.14.tar.gz tar zxvf redis-6.2.14.tar.gz cd redis-6.2.14 # 编译安装,指定安装目录 make make install PREFIX=/usr/local/redis编译前需要确保系统有gcc和make工具,缺少的话先安装:
yum install -y gcc gcc-c++ make # CentOS/RHEL系列 # 或者 apt install -y build-essential # Debian/Ubuntu系列编译过程中如果碰到jemalloc相关的报错,通常是内存分配器的问题,在make命令后面加MALLOC=libc就能绕过:
make MALLOC=libc2.3 目录规划和系统参数调整
安装完成后,目录结构我习惯这样组织,方便后续维护和备份:
/usr/local/redis/ # Redis安装目录 /usr/local/redis/bin/ # 可执行文件 /etc/redis/6379.conf # 实例配置 /etc/redis/6380.conf # 实例配置 /data/redis/6379/ # AOF持久化文件目录 /data/redis/6380/ # AOF持久化文件目录 /data/redis/logs/ # 日志目录在启动集群前,系统层面还有几个参数需要调整,因为集群节点之间需要通过总线端口(Cluster Bus)通信,且Redis的网络连接数可能会很高:
- 端口范围:Cluster模式下,每个节点除了对外服务的端口6379,还会监听一个端口+10000的集群总线端口(即16379),用于节点间的心跳检测、数据迁移等内部通信。所以安全组和防火墙不能只放行6379,16379也务必放行。
- 文件描述符限制:Redis作为高并发服务,连接数可能非常大,建议把
/etc/security/limits.conf里的文件句柄数调高:
# /etc/security/limits.conf 中追加 * soft nofile 65535 * hard nofile 65535- TCP内核参数:如果遇到大量TIME_WAIT连接的问题,可以适当调整内核参数,但这个我是在压测验证阶段才遇到的,后面专门讲。
防火墙放行命令以firewalld为例:
firewall-cmd --permanent --add-port=6379/tcp firewall-cmd --permanent --add-port=16379/tcp firewall-cmd --reload这一层准备工作看似琐碎,但是不做的话,后面集群创建时会反复出现节点握手失败、cluster状态异常的问题,排查起来很痛苦。
3. 核心配置与节点启动:每一行配置都要能说出为什么
3.1 redis.conf里的关键配置项拆解
每个Redis实例都需要一份独立的配置文件。这里我以6379端口的实例为例,把关键配置项逐一说明,大家抄作业的同时也理解每个参数的用途:
# /etc/redis/6379.conf port 6379 daemonize yes pidfile /var/run/redis_6379.pid logfile "/data/redis/logs/redis_6379.log" dir /data/redis/6379 # 集群开关 cluster-enabled yes cluster-config-file nodes-6379.conf cluster-node-timeout 15000 # 持久化配置 appendonly yes appendfsync everysec # 内存与淘汰策略 maxmemory 4gb maxmemory-policy allkeys-lru # 网络安全 bind 0.0.0.0 protected-mode yes requirepass MyRedisCluster@2024 masterauth MyRedisCluster@2024逐项解释一下核心参数:
cluster-enabled yes:这个是总开关,开启后Redis才会以Cluster模式运行。填no的话,即使你后面用cluster相关命令,也会报错提示集群模式未启用。
cluster-config-file nodes-6379.conf:这个文件由Redis自己维护,记录的是当前节点视角下的集群拓扑信息,包括哪些节点在线、槽位分配等。文件不要手动编辑,Redis启动时会自动生成和更新。注意不同实例的这个文件名必须不同,因为每个节点都有自己的视角。
cluster-node-timeout 15000:这个参数定义节点间通信的超时时间,单位是毫秒。如果一个主节点超过这个时间没有响应,集群会判定该节点故障并触发故障转移。设置太短容易误判(网络抖动就触发切换),设置太长则故障恢复慢。我最终稳定在了15秒,兼顾了两者。
appendonly yes和appendfsync everysec:集群模式下的持久化建议开AOF,每秒写一次。为什么集群了还要持久化?因为节点挂了重启后,如果能从本地恢复数据,就不需要从主节点全量同步,恢复速度会快很多,对集群的冲击也更小。
maxmemory 4gb:限制每个实例最大内存。配合allkeys-lru淘汰策略,保证在内存满的时候不会因为无法写入而拖垮整个集群。这块需要根据实际机器内存合理规划,我建议给操作系统的剩余内存和redis的持久化磁盘IO留出余量,不要顶满。
requirepass和masterauth:这是最容易忽略的地方。集群模式下,主从节点之间也需要做同步,从节点Promote为主节点后,要用masterauth中配置的密码去连接新的主节点。如果只设置了requirepass而不设置masterauth,主从切换或者重新建立复制关系时,从节点会因为认证失败而一直无法同步数据。
3.2 多实例部署时注意端口和目录隔离
如果一台机器上跑多个Redis实例(比如我这里的6379和6380),第二份配置和第一份的区别主要是这些:
# /etc/redis/6380.conf port 6380 pidfile /var/run/redis_6380.pid logfile "/data/redis/logs/redis_6380.log" dir /data/redis/6380 cluster-config-file nodes-6380.conf其余参数保持一致即可,不需要额外改动。这里容易出问题的是dir目录,如果两个实例共用同一个持久化目录,AOF文件和RDB文件会互相覆盖,数据会出大乱子。这个我在初期搭建多实例时确实踩过,后来强制约定“一个实例一个目录”,再没出过问题。
3.3 启动节点并确认集群模式生效
配置文件写好后,逐个启动各节点:
/usr/local/redis/bin/redis-server /etc/redis/6379.conf /usr/local/redis/bin/redis-server /etc/redis/6380.conf启动后用redis-cli进入实例,执行cluster info看看状态:
/usr/local/redis/bin/redis-cli -p 6379 -a MyRedisCluster@2024 127.0.0.1:6379> cluster info cluster_state:fail cluster_slots_assigned:0 cluster_slots_ok:0 cluster_known_nodes:1 cluster_size:0这个时候集群状态是fail,不用慌,因为还没把各个节点关联起来,节点数也只是自己一个,槽位分配为0。确认集群模式已经开启后,接下来进入建集群的关键步骤。
重要提示:启动命令如果用了
daemonize yes,Redis会以守护进程在后台运行,记得看日志确认正常起来。如果启动报错,第一步一定是看日志文件,把logfile里的内容截图下来对照排查,不要瞎猜。
4. 集群创建与各项验证:从初始化到故障演练
4.1 一条命令完成集群初始化
Redis官方提供了redis-cli --cluster create命令来创建集群,这个命令会自动完成节点握手和槽位分配,非常方便。执行方式如下:
/usr/local/redis/bin/redis-cli --cluster create \ 192.168.1.101:6379 192.168.1.102:6379 192.168.1.103:6379 \ 192.168.1.101:6380 192.168.1.102:6380 192.168.1.103:6380 \ --cluster-replicas 1 \ -a MyRedisCluster@2024--cluster-replicas 1的含义是每个主节点配置1个从节点。命令执行后,redis-cli会先检查节点状态,然后给出一个槽位分配方案,会显示类似这样的计划:
>>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 192.168.1.102:6380 to 192.168.1.101:6379 Adding replica 192.168.1.103:6380 to 192.168.1.102:6379 Adding replica 192.168.1.101:6380 to 192.168.1.103:6379注意它默认分配的从节点是比较合理的——这里可以看到101的从节点在102上,102的从节点在103上,103的从节点在101上,每个从节点都和主节点不在同一台机器,这正是我前面规划的跨机冗余。
命令中间会询问Can I set the above configuration? (type 'yes' to accept),这时候输入yes回车,集群就开始握手并分配槽位。成功后会看到“All 16384 slots covered”的提示,说明整个集群已经健康运行:
[OK] All 16384 slots covered.4.2 用cluster info和cluster nodes验证集群状态
集群建好后,第一件事是确认集群状态。连到任意一个节点执行:
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_state:ok:集群处于正常状态。如果是fail,说明槽位不完整或有节点不可达。cluster_slots_assigned:16384:全部16384个槽位都已分配。cluster_known_nodes:6:集群已知节点数为6个。cluster_size:3:集群主节点数量为3。
再执行cluster nodes,能看到完整的节点拓扑和角色信息:
127.0.0.1:6379> cluster nodes输出会展示每个节点的ID、IP:端口、角色(master或slave)、负责的槽位范围,以及主从绑定关系。比如看到类似这样的行,说明该节点是主节点且连接正常:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 192.168.1.101:6379@16379 myself,master - 0 0 1 connected 0-5460和从节点:
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy 192.168.1.102:6380@16380 slave zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz 0 0 1 connected注意,这里的@16379就是前面的集群总线端口,每个节点后面的端口号是它的总线端口,不是业务端口。看到这些信息基本就能确认集群拓扑是符合预期的。
4.3 数据读写验证:搞懂CRC16槽位计算
集群状态OK了,接下来就是验证读写链路。Cluster模式下,客户端连接任意节点,如果key对应的槽位不在当前节点上,节点会返回MOVED重定向错误,客户端库会自行处理跳转。我们先手动验证一下这个机制能正常工作。
先在一个主节点上写入数据:
127.0.0.1:6379> set user:1001 "zhangsan" OK 127.0.0.1:6379> get user:1001 "zhangsan"接着在另一个主节点上读取同一个key,看看会发生什么:
127.0.0.1:102:6379> get user:1001 (error) MOVED 10439 192.168.1.101:6379返回的MOVED信息表示:keyuser:1001对应的槽位是10439,这个槽位在192.168.1.101:6379节点上。这说明集群的数据路由逻辑正常工作。正常情况下,我们用的客户端SDK(比如Java的Jedis、Lettuce,Python的redis-py)都会自动处理MOVED指令,对应用层是透明的,但如果用的是redis-cli,需要加-c参数开启集群模式,否则会直接报MOVED错误:
/usr/local/redis/bin/redis-cli -c -p 6379 -a MyRedisCluster@2024-c参数让redis-cli自动跟随节点的重定向指示,这样跨节点读写就不用手动指定了。
关于槽位计算,可以顺手做个验证。Redis Cluster用CRC16算法对key取CRC16值,然后对16384取模,得到槽位号。比如上面的user:1001落到槽位10439,就是这个计算的结果。这个计算过程不用自己实现,客户端库都帮你做了,但理解这个概念有助于排查为什么某些key会落在某个节点上。
4.4 故障转移验证:手动关一个主节点
集群的高可用能力,不能停留在“理论上有”这个层面,必须实操验证。我的验证方法是直接模拟主节点宕机,观察集群是否能在阈值时间内自动完成故障转移。
选定192.168.1.101:6379这个主节点,把它直接停掉:
/usr/local/redis/bin/redis-cli -p 6379 -a MyRedisCluster@2024 shutdown nosave然后观察集群日志和节点状态。正常情况下,经过cluster-node-timeout(我设置的15秒)后,集群会把这个主节点标记为fail,并触发故障转移——它对应的从节点(我这里是192.168.1.102:6380)会被提升为新的主节点。
用cluster nodes查看集群状态:
127.0.0.1:102:6379> cluster nodes会看到原本的从节点角色变成了master,而且接管了原主节点负责的槽位。这说明自动故障转移成功。整个过程大概20秒左右,符合预期。
验证完毕后,再把原来的主节点恢复启动。这里有个细节需要注意:它启动后不会立刻恢复成主节点,而是作为新的从节点挂到当前的主节点下面,重新开始数据复制。这个行为是集群自动处理的,对上层应用完全透明。
如果你在生产环境做类似的故障演练,建议在业务低峰期进行,并且提前通知相关团队。虽然故障转移对客户端基本无感知,但节点重启的瞬间,连接池建连会有短暂的耗时波动。
4.5 在线扩缩容:水平扩展不是说说而已
集群搭建好,验证了基本功能和高可用性,还不够,还要验证集群的扩展能力。Redis Cluster支持在线增加节点和删除节点,整个过程不需要停机。
新增一个节点时,我习惯用一个独立的端口,比如在服务器A上再起一个6381端口实例,配置文件和前面一样,只是端口和目录不同。启动后先把它加入到集群:
/usr/local/redis/bin/redis-cli --cluster add-node \ 192.168.1.101:6381 \ 192.168.1.101:6379 \ -a MyRedisCluster@2024这个命令会以6379作为“介绍人”,把6381节点加入集群。加入后,新节点默认是不负责槽位的,需要手动执行reshard来分配槽位:
/usr/local/redis/bin/redis-cli --cluster reshard \ 192.168.1.101:6379 \ --cluster-from all \ --cluster-to <新节点ID> \ --cluster-slots 4096 \ -a MyRedisCluster@2024因为集群总共16384个槽位,原来3个主节点平均分配,新增一个主节点后,理想情况是每个节点4096个槽位。所以这里把4096个槽位从所有老节点迁移到新节点。执行过程中会有交互式确认,跟着提示走就行。
迁移完成后,再用cluster nodes查看,确认新节点已经接管了部分槽位,且原有节点各自的槽位数减少了。这就是水平扩容的完整过程。
4.6 压测验证:让数据说话
最后一项验证是压测。Redis自带redis-benchmark工具,可以方便地模拟并发读写。我用它跑了两个场景:单节点基准压测和集群压测,对比结果来确认集群模式下的性能损耗在可接受范围。
单节点压测:
/usr/local/redis/bin/redis-benchmark -h 192.168.1.101 -p 6379 \ -a MyRedisCluster@2024 -c 200 -n 1000000 -t set,get -d 100集群压测需要用集群模式:
/usr/local/redis/bin/redis-benchmark -h 192.168.1.101 -p 6379 \ -a MyRedisCluster@2024 -c 200 -n 1000000 -t set,get -d 100 --cluster实测下来,3主3从的集群在并发200、百万级操作请求下,QPS轻松突破10万+,响应时间P99在个位数毫秒级别。和生产单节点相比,因为数据分散到了多个节点,性能不降反升。不过这里有个点要注意:redis-benchmark的--cluster模式依赖一个与集群节点的交互能力,如果你的安装在老版本上,可能不支持该参数,需要手动指定不同的节点来模拟请求分散效果。
压测结束后,记得查看机器的CPU、内存和网络负载,确认集群的瓶颈在哪里。通常瓶颈会出现在网卡或者CPU单核上,因为Redis是单线程模型,IO多线程默认没开启的话,多核利用不充分。如果CPU还没跑满但网卡先满了,就该考虑扩容机器或者增加网卡带宽了。
5. 常见问题与排查技巧实录
5.1 集群状态卡在fail:先查槽位分配和节点握手
cluster_state:fail是最常见的集群异常状态。原因通常有两类:一是槽位没有完全覆盖(上面提到刚创建集群时是正常的0/16384),二是集群中存在不可达节点。
排查思路是这样的:先看cluster_nodes输出,找到标记为disconnected的节点,确认它的IP:端口是否还能ping通;如果网络没问题,再看集群总线端口(端口+10000)是否被防火墙拦截。我之前排查过一个案例,业务端口6379通了,但总线端口16379没放行,节点之间无法通信,集群一直报fail。
处理方式很简单,放行总线端口后,再使用cluster meet命令让节点重新握手,等几秒后集群状态会自动恢复。
5.2 主从同步失败:八成是masterauth的锅
用着用着发现某个从节点数据一直不同步,查看复制状态:
127.0.0.1:6380> info replication如果master_link_status:down,并且日志里反复出现MASTER aborted replication with an error: NOAUTH Authentication required,那基本可以确认是masterauth没配置或者密码不对。
上面配置时我强调过,requirepass和masterauth必须同时设置,就是防这个坑。处理方法是:在从节点的配置文件里补上正确的masterauth,重启从节点实例,复制关系就会自动恢复。
生产环境还有一个隐蔽场景:业务方修改了集群密码,结果只改了requirepass,忘了同步改masterauth,导致整个集群的复制链路全部断裂。这个坑我见人踩过,所以集群密码变更的checklist里一定要包含这两项。
5.3 key分布不均匀导致热点问题
集群模式下,所有key被平均分布到16384个槽位,理论上数据是均衡的。但实际业务中,某些key的访问频率可能远高于其他key,造成单节点热点,拖累整个集群吞吐量。比如一个秒杀场景下的热卖商品ID,所有请求都打向同一个key,对应的槽位和节点负载就特别高。
解决办法是给这些热点key加随机后缀,把请求分散到不同的key上(比如product:1001拆成product:1001:0到product:1001:9十个key),这样它们的槽位散落到不同节点,压力自然分散。代价是应用层需要多维护一层映射关系,读的时候要把所有后缀key都查一遍。这个方案虽然土,但非常有效,是处理key热点问题的常用手段。
另外,Redis Cluster还支持hash tag机制,可以让某些key强制落在同一个槽位。它的用法是:如果key中含有{},Redis只对花括号内部的内容计算CRC16。比如user:{1001}:profile和user:{1001}:orders这两个key,会落在同一个槽位。这在需要批量操作多个key(比如MGET)时非常有用,因为Cluster模式下跨槽位的多key操作是不被保证原子性的,而同一个槽位内可以。
5.4 内存淘汰策略没设好导致写失败
如果你在配置里没有设置maxmemory和maxmemory-policy,Redis默认情况下内存满了就直接拒绝写入,报错OOM command not allowed when used memory > 'maxmemory'。
集群模式下这个问题的危害更大,因为单个节点满了,该节点负责的槽位无法写入,等于整个集群的部分写入功能降级。我在配置里已经给出了推荐:maxmemory 4gb+allkeys-lru。前者是内存水位,后者是淘汰策略,表示内存满时优先淘汰最近最少使用的key。
这里有一个取舍:allkeys-lru会淘汰所有key,包括一些可能还需要的数据;如果需要更精细的控制,可以改用volatile-lru,只淘汰设置了过期时间的key。但前提是你的业务key绝大多数都设置了TTL,不然会导致内存无法释放。我这边业务上大量key本来就有过期时间,所以用volatile-lru会更合适,但为了安全兜底,最终选了allkeys-lru,至少保证写入一直可用。
5.5 大量TIME_WAIT连接导致端口耗尽
压测时遇到一个有趣的问题:一段时间后,客户端连接Redis报错Cannot assign requested address。排查后发现是客户端机器的TIME_WAIT状态连接堆积太多,导致端口被耗尽了。
这是因为高并发下,客户端每次连接Redis后快速断开,大量的TCP连接进入TIME_WAIT状态,要等2MSL(通常60秒)才能释放。压测期间QPS高,这个堆积速度远超释放速度。
解决办法有几个方向:
- 客户端使用连接池,不要每次请求都新建连接。大部分语言SDK默认维护连接池,确保池大小设置合理即可。
- 调整客户端的TCP参数,允许端口复用:
sysctl -w net.ipv4.tcp_tw_reuse=1- 缩短TIME_WAIT等待时间:
sysctl -w net.ipv4.tcp_fin_timeout=15这是压测场景下的常用手段。在正式环境,重点是确保连接池配置正确,而不是无脑调内核参数。
5.6 常见问题速查表
| 现象 | 可能原因 | 排查与处理 |
|---|---|---|
| 集群状态为fail | 槽位未分配完整、节点不可达 | 执行cluster nodes检查断连节点,确认总线和业务端口都放通 |
| 从节点数据不同步 | masterauth密码不匹配 | 检查info replication,确认master_link_status,修正masterauth后重启 |
| 写操作报MOVED错误 | 客户端未启用集群模式 | 客户端连接时开启集群模式(redis-cli加-c) |
| 内存满了无法写入 | maxmemory和淘汰策略未配置 | 设置合理的maxmemory和maxmemory-policy |
| 连接数过多触发TIME_WAIT | 客户端未使用连接池 | 优化连接池配置,必要时调内核TCP参数 |
| 故障转移不触发 | cluster-node-timeout设置过大 | 根据业务容忍度适当调小该参数 |
| 扩容后槽位不均衡 | reshard操作不完整 | 重新执行reshard,合理分配迁移槽位数 |
6. 个人经验与扩展建议
集群搭建完成并全部验证通过后,我最大的体会是:搭建本身并不难,真正花时间的是理解集群机制、设计合理的拓扑,以及把故障转移、扩缩容这些运维动作都演练到位。
有几个点想单独再强调一下。
第一,集群上线前一定要把监控做起来。我这边用Prometheus + redis_exporter采集每个集群节点的指标,包括内存使用率、命中率、主从复制延迟、槽位分布情况等。一旦某个节点的内存增长曲线异常,或者复制延迟持续拉高,监控告警会先于用户反馈发现问题。集群不是搭完就万事大吉,后续的日常巡检才是保障稳定性的关键。
第二,备份策略不能因为集群就放松。有些朋友觉得“集群里每个主节点都有从节点,数据肯定安全”,这种想法很危险。从节点同步的数据是实时的,但如果发生逻辑错误(比如误执行了FLUSHALL、业务代码批量删除),错误操作会立刻被复制到所有从节点,集群的冗余保护是防不住这种人为事故的。所以集群模式下依然要定期做RDB备份,而且备份要存放在独立的存储上,不能和Redis节点在同一台机器。
第三,升级和运维操作要有预案。比如Redis大版本升级,我一般会先在测试集群上完整跑一遍验证流程,确认兼容性后再动生产。扩缩容操作尽量在业务低峰期执行,并且提前确认客户端SDK支持集群模式,避免因为客户端不支持MOVED重定向导致线上故障。
后续如果你们的Redis集群规模再往上走,还可以考虑引入官方提供的Redis Cluster Manager(redis-cli --cluster)之外的工具链,比如一些图形化管理面板,能直观看到每个节点的槽位和主从关系。但所有自动化和工具都建立在基础原理之上,把这台集群彻底搞透,后面再怎么变都不慌。