一、先别慌,故障排查有章法
当我们发现Redis集群响应变慢、连接失败,甚至直接“罢工”时,第一反应往往是紧张。但请记住,任何复杂的系统故障,排查起来都有其规律。我们的目标不是盲目地重启服务,而是像侦探一样,根据线索找到问题的根源。
排查Redis集群故障,可以遵循一个从外到内、从现象到本质的流程:
- 现象确认:是全部应用都报错,还是部分?错误信息是什么?(如
CLUSTERDOWN,MOVED,连接超时) - 网络与连接层检查:应用和Redis节点之间网络通吗?防火墙规则对吗?
- 集群状态诊断:集群本身健康吗?主从关系正常吗?
- 节点深度检查:具体是哪个节点出了问题?是内存、CPU、磁盘还是配置问题?
- 数据与命令分析:是否执行了异常命令或遇到了特殊数据?
下面,我们就按照这个思路,一步步拆解常见问题。
二、连接都失败了?先看网络和配置
应用连不上Redis集群,这是最直接的症状。别急着怀疑Redis,先从最简单的开始。
场景:你的Java应用突然大量抛出Connection refused或Connection timeout异常。
排查步骤:
- 检查基础网络:从应用服务器,用
ping和telnet命令测试是否能通Redis节点的IP和端口。如果网络不通,问题可能出在云服务器安全组、防火墙(iptables, firewalld)或物理网络设备上。 - 检查客户端配置:这是最常见的问题源。确保你的客户端配置的是集群中所有主节点的地址列表,而不是仅仅一个。客户端驱动会通过其中一个节点获取整个集群的拓扑信息。
技术栈:Java (使用Jedis客户端)
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.util.HashSet;
import java.util.Set;
public class RedisClusterDemo {
public static void main(String[] args) {
// 错误示例:只配置了一个节点地址
// Set<HostAndPort> jedisClusterNodes = new HashSet<>();
// jedisClusterNodes.add(new HostAndPort("192.168.1.100", 6379)); // 如果这个节点恰好故障,整个客户端就无法初始化
// 正确示例:配置所有已知的主节点地址(至少2-3个)
Set<HostAndPort> jedisClusterNodes = new HashSet<>();
jedisClusterNodes.add(new HostAndPort("192.168.1.100", 6379)); // 主节点1
jedisClusterNodes.add(new HostAndPort("192.168.1.101", 6379)); // 主节点2
jedisClusterNodes.add(new HostAndPort("192.168.1.102", 6379)); // 主节点3
// 注意:配置的是端口,通常是客户端端口(如6379),而不是集群总线端口(+10000,如16379)
try (JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes)) {
// 进行一些操作测试...
jedisCluster.set("diagnose:key1", "Hello Cluster");
String value = jedisCluster.get("diagnose:key1");
System.out.println("获取到的值: " + value);
} catch (Exception e) {
e.printStackTrace(); // 连接失败会在这里抛出异常
}
}
}
关联技术:JedisCluster的工作原理
JedisCluster在初始化时,会随机选择一个配置的节点连接,并发送CLUSTER SLOTS命令来获取整个集群的槽位(slot)分配信息和所有节点地址。之后,它会为每一个主节点建立一个连接池。当你要执行一个命令(如set key value)时,客户端会先计算这个key属于哪个槽(使用CRC16算法),然后直接向管理该槽的节点发起请求。这就是为什么你必须配置多个节点地址——为了在初始化时即使有节点宕机也能获取到集群信息。
三、集群状态不健康?CLUSTER命令是你的听诊器
当连接建立后,仍然遇到CLUSTERDOWN错误或性能低下,说明集群内部状态可能有问题。我们需要登录到Redis节点,使用Redis自带的集群管理命令进行诊断。
核心诊断命令:
redis-cli -c -h [host] -p [port] cluster info:查看集群整体状态。重点关注cluster_state:ok,如果是fail,则集群已下线。redis-cli -c -h [host] -p [port] cluster nodes:查看集群所有节点的详细视图。这是最强大的工具。
场景分析:假设我们有一个3主3从的集群,端口从7001到7006。
技术栈:Redis CLI
# 连接到集群中任意一个节点,这里用7001
$ redis-cli -c -h 192.168.1.100 -p 7001
# 执行 cluster info 命令
192.168.1.100:7001> cluster info
cluster_state:ok # 状态正常,如果是 cluster_state:fail 就严重了
cluster_slots_assigned:16384 # 已分配的槽位数,必须是16384才完整
cluster_slots_ok:16384 # 状态为OK的槽位数,应等于assigned
cluster_slots_pfail:0 # 可能失效(PFAIL)的槽位数
cluster_slots_fail:0 # 已确认失效(FAIL)的槽位数,大于0就有问题
cluster_known_nodes:6 # 集群已知节点数,核对是否与预期一致
cluster_size:3 # 集群中至少有一个槽的主节点数量,即主节点数
... (其他输出略)
# 执行 cluster nodes 命令,输出经过格式化以便阅读
192.168.1.100:7001> cluster nodes
# 输出示例(每一行是一个节点):
# 节点ID | IP:端口@集群总线端口 | 标志 | 主节点ID | 最后PING时间 | 最后PONG时间 | 配置纪元 | 连接状态 | 负责的槽位范围
#
# 1) df1d... 192.168.1.100:7001@17001 myself,master - 0 1680000000000 1 connected 0-5460
# 解释:这是当前连接节点(myself),是主节点(master),没有主节点(-),负责槽位0-5460。
#
# 2) a1b2... 192.168.1.100:7002@17002 master - 0 1680000000100 2 connected 5461-10922
# 解释:另一个主节点,负责槽位5461-10922。
#
# 3) c3d4... 192.168.1.100:7003@17003 master - 0 1680000000200 3 connected 10923-16383
# 解释:第三个主节点,负责槽位10923-16383。
#
# 4) e5f6... 192.168.1.101:7004@17004 slave df1d... 0 1680000000300 1 connected
# 解释:这是一个从节点(slave),它的主节点ID是`df1d...`(即7001节点)。
#
# 5) 7g8h... 192.168.1.101:7005@17005 slave a1b2... 0 1680000000400 2 connected
# 6) 9i0j... 192.168.1.102:7006@17006 slave c3d4... 0 1680000000500 3 connected
如何解读cluster nodes输出并排查问题:
- 主从关系断裂:如果某个
slave行的主节点ID是一个不存在的节点ID,或者对应的master行标志是fail,则说明主从复制断开。需要检查从节点日志,常见原因有网络中断、主节点长时间阻塞导致复制超时。 - 节点失效(PFAIL/FAIL):在节点标志中看到
fail?(PFAIL)或fail(FAIL)。fail?是单个节点认为另一个节点失联,是怀疑状态;fail是集群大多数节点确认该节点失效,是确定状态。一个主节点被标记为fail且其从节点存在,则会触发故障转移,其中一个从节点会升级为主节点。如果fail的主节点没有从节点,那么它负责的槽位数据将不可用,集群状态可能变为fail。 - 槽位分配不完整:
cluster_slots_assigned不是16384。可能因为节点增减、槽位迁移(reshard)过程中断导致。需要手动进行槽位修复,这是一个高级操作。
四、单个节点出问题?深入内部找原因
如果集群状态显示是ok的,但应用访问特定数据时很慢或报错,可能是某个具体节点负载过高或存在内部问题。
排查步骤:
- 检查资源使用率:登录到疑似问题节点的服务器。
top或htop命令:看Redis进程的CPU和内存占用。Redis是单线程的,如果CPU持续100%,说明正在执行一个非常耗时的命令。free -h命令:看系统整体内存。如果Redis内存使用接近或超过maxmemory配置,会触发逐出(Eviction)或导致写命令失败。
- 查看Redis节点日志:Redis日志(通常配置在
/var/log/redis/redis-server.log或通过redis.conf的logfile指定)包含宝贵信息。- 搜索
OOM(内存不足)、Background saving error(持久化错误)、Connection with slave x.x.x.x lost(主从连接丢失)。
- 搜索
- 使用Redis监控命令:
redis-cli -h [host] -p [port] info stats:查看命令统计、网络流量等。redis-cli -h [host] -p [port] info persistence:查看RDB/AOF持久化状态,如果rdb_last_bgsave_status:err,说明上次后台保存失败。redis-cli -h [host] -p [port] slowlog get 10:非常有用! 获取最近10条慢查询日志。Redis可以配置slowlog-log-slower-than(如10000微秒,即10毫秒)来记录执行时间超过阈值的命令。
场景示例:排查慢查询
技术栈:Redis CLI
# 假设我们怀疑7002节点响应慢
$ redis-cli -h 192.168.1.100 -p 7002
# 1. 查看当前慢查询配置
192.168.1.100:7002> config get slowlog*
1) "slowlog-max-len" # 慢查询日志最大长度,只保留最近的N条
2) "128"
3) "slowlog-log-slower-than" # 慢查询阈值,单位微秒(μs),10000μs=10ms
4) "10000"
# 2. 获取最近的慢查询
192.168.1.100:7002> slowlog get 5
1) 1) (integer) 14 # 慢查询日志的唯一ID
2) (integer) 1680000123 # 该命令执行时的时间戳(Unix时间戳)
3) (integer) 50234 # 命令执行耗时,单位微秒(50234μs ≈ 50ms)
4) 1) "KEYS" # 命令和参数列表
2) "user:session:*"
5) "192.168.2.50:55342" # 发起该命令的客户端地址
6) "" # 客户端名称(如果通过CLIENT SETNAME设置过)
# 3. 分析:发现一条耗时50ms的`KEYS`命令。`KEYS`命令在生产环境是危险的,
# 因为它会遍历所有键,在数据量大的时候会阻塞Redis单线程,导致其他命令排队。
# 解决方案:使用`SCAN`命令迭代代替,或者为需要查找的键建立索引模式(如使用Set存储键名)。
五、数据异常与特殊命令陷阱
有些问题与集群状态无关,而是源于数据本身或使用了不兼容的命令。
- 跨槽位操作命令:在集群模式下,多个key的操作(如
MGET,MSET,SINTER)要求所有key必须位于同一个槽位,否则会返回CROSSSLOT错误。解决方案是使用Hash Tag,即用{}将key的一部分括起来,Redis只会对{}内的内容计算槽位。例如,user:{1000}:name和user:{1000}:email会被分配到同一个槽位。 - 阻塞命令导致的高延迟:
BLPOP、BRPOP等阻塞命令如果长时间没有数据,会导致连接阻塞。大量连接同时阻塞会消耗资源。需要评估阻塞超时时间,并确保消息生产者正常。 - 大Key和热Key:
- 大Key:指一个key对应的value非常大(如一个包含百万元素的List/Hash)。在迁移、持久化、删除(
DEL)时会引起长时间阻塞。使用redis-cli --bigkeys扫描(注意在生产环境慎用,会影响性能)。 - 热Key:指某个key被异常高频地访问,超过了单个节点的处理能力。解决方案:在客户端做本地缓存、使用Redis的
MONITOR命令定位、或者将热Key拆分成多个子Key(如hotkey:1,hotkey:2)进行分片。
- 大Key:指一个key对应的value非常大(如一个包含百万元素的List/Hash)。在迁移、持久化、删除(
应用场景与注意事项
应用场景:Redis集群主要用于解决海量数据缓存和高并发访问场景下的单点瓶颈和容量限制问题,例如大型电商网站的购物车、会话存储、排行榜、秒杀库存等。
技术优缺点:
- 优点:高可用(自动故障转移)、线性扩展(增加节点可提升容量和性能)、数据分片。
- 缺点:架构复杂,部署和维护成本高;不支持跨节点事务和跨key的聚合操作;客户端需要支持集群协议;迁移和扩容需要谨慎操作。
注意事项:
- 规划是关键:在搭建集群前,合理规划节点数量、内存大小、主从比例。
- 监控不能少:必须配备完善的监控,监控集群状态、节点资源、慢查询、Key数量、内存碎片率(
mem_fragmentation_ratio)等指标。 - 备份与持久化:即使有集群,也要定期备份RDB/AOF文件到异地。配置合理的
appendfsync策略以平衡性能和数据安全。 - 避免满负荷运行:
maxmemory不要设置为物理内存的100%,要留有余地给系统和其他进程,通常建议80%左右。
文章总结:
Redis集群故障诊断是一个系统工程,需要冷静、有序地进行。从客户端的连接配置入手,检查网络和基础设置;然后利用cluster info和cluster nodes这两个核心命令对集群进行“体检”,判断整体健康度和主从关系;接着深入到具体节点,检查资源、日志和慢查询,定位性能瓶颈或内部错误;最后,要警惕数据模式和命令使用上的陷阱,如跨槽操作、大Key热Key等。建立完善的监控告警体系,防患于未然,远比事后救火更重要。记住,理解Redis集群的工作原理(如槽位分配、Gossip协议、故障转移共识)是进行有效诊断的基础。
评论