☰
Redis集群部署实战:从单机迁移到高可用架构
2026/10/22 4:45:57 网站建设 项目流程

最近正好接手一个数据量往上涨的业务系统,单机Redis内存眼看着不够用,读写瓶颈也越来越明显。借着这个机会,我把从单机到Redis集群的完整迁移过程走了一遍,顺便把踩过的坑、调过的参数、验证过的主从切换流程都记录了下来。这篇文章就以“Linux部署Redis集群”为主线,从架构选型讲到配置落地,再聊到日常运维里的坑,尽量把关键决策背后的理由讲清楚,而不是只丢一份配置让你照抄。适合正准备从单机切到集群、或者想自己搭一套Redis集群练手的同学参考。

1. 部署前的架构思考与方案选型

1.1 Redis集群到底解决了什么问题

先聊聊为什么要上集群。单机Redis遇到的压力通常来自三个方向:一是内存容量,数据量超过单机物理内存后只能靠淘汰策略硬撑,对业务影响很大;二是CPU性能,Redis虽然是单线程模型,但高并发下CPU依然会满;三是可用性,单点挂了整个缓存层就断了,后端数据库会直接被流量打穿。

Redis集群方案提供的核心能力有三个:数据分片、水平扩展、高可用。数据分片是通过哈希槽(hash slot)机制实现的,整个集群有16384个槽位,每个key经过CRC16校验后映射到某个槽,再由槽分配到具体节点。写入时只需要连接任意一个节点,命中哪个槽就在哪个节点上读写。水平扩展体现在集群可以动态增加节点,把槽和对应数据迁移过去。高可用体现在每个主节点可以挂一个或多个从节点,主节点挂了从节点自动提升。

单机、哨兵模式、集群模式怎么选?我的建议是:数据量在几十GB以内、QPS压力可控、能接受秒级自动切换,哨兵模式完全够用;如果数据量会持续增长、需要在线扩容、或者对吞吐量有硬性要求,直接上集群模式。集群模式虽然对多key操作有限制,但带来的横向扩容能力是哨兵模式不具备的。

1.2 集群规模与拓扑设计

Redis集群要求至少3个主节点,官方推荐每个主节点配1个从节点,即3主3从是最小的高可用集群。生产环境我建议至少6台独立节点,或者3台物理机上各跑2个Redis实例(注意端口区分)。如果资源紧张,可以先3主3从起步,后面在线扩容。

关于节点分布,有一点要特别注意:从节点尽量不要和对应主节点放在同一台物理机上,否则物理机宕机时主从一起挂,高可用就成空话。我自己最初图省事,把3主3从都塞在2台机器上,后来做容灾演练时一拔电源,直接有两个分片完全不可用,教训很深刻。

另一个要考虑的是cluster-node-timeout。这个参数决定了节点间心跳超时多久会被判定为故障。默认15000毫秒,生产环境我通常设为5000到10000之间。设太短,网络抖动就会触发无谓的主从切换;设太长,故障恢复时间变长。可以在部署后用ping来测量节点间的网络延迟,再根据延迟倍数调整。

2. 环境准备与Redis安装

2.1 服务器规划与系统参数调优

我先用6台虚拟机模拟6个独立节点,每台分配2核CPU、4GB内存,操作系统是常见的Linux发行版。用虚拟机部署有个好处,可以随时快照回滚,做故障演练也不用担心影响真实业务。

安装Redis之前,有几个系统参数必须先调,否则集群跑起来会出各种诡异问题:

  • vm.overcommit_memory设置为1。Redis的持久化(RDB和AOF)涉及fork操作,如果内存过度分配策略设置不当,fork可能失败或者进程被系统杀掉。这个参数的含义是允许所有内存分配请求,不检查实际可用内存,避免Redis在fork时被OOM killer误杀。
  • 关闭透明大页THP。透明大页会导致Redis的写时复制(copy-on-write)机制下内存占用爆增,延迟也会升高。需要修改内核参数,或者直接设置启动命令禁用。
  • net.core.somaxconn设置为1024以上。Redis默认监听511个连接请求队列,如果系统默认的somaxconn小于511,高并发下客户端连接会被内核直接丢弃,表现就是偶尔连接超时。
  • 文件描述符上限调高。建议设置到65535以上,否则大量客户端连接时会出现 "Can't open so many files" 之类的报错。

调整方式直接修改/etc/sysctl.conf然后执行sysctl -p使其生效,同时把/etc/security/limits.conf里的nofile限制调高。这几步看着繁琐,但漏掉任何一个,后边排查问题都可能花掉半天时间。

2.2 Redis源码编译安装的几个细节

我习惯使用官方源码编译安装,而不是直接用系统自带的软件包,因为版本比较灵活,可以精确控制。Redis编译依赖gcc、make这些基础工具,先安装环境,再下载源码包解压编译:

# 安装编译依赖 yum install -y gcc make # 下载源码包(版本号按需替换) wget http://download.redis.io/releases/redis-7.0.14.tar.gz tar xzf redis-7.0.14.tar.gz cd redis-7.0.14 # 编译安装到指定目录 make make install PREFIX=/usr/local/redis

编译时有个小坑:如果gcc版本较老,编译可能会报错,提示malloc、backtrace相关的符号找不到。这时先执行make distclean清掉之前的环境,再重新编译,或者升级gcc版本。另外,编译之前建议阅读源码包里的README,它会明确列出当前版本要求的编译环境。

安装完成后,可执行文件会放在/usr/local/redis/bin下。我给每个节点建了独立的目录结构,包括数据目录、日志目录和配置文件目录,后面管理起来清晰很多:

mkdir -p /data/redis/{data,logs,conf,run}

每个实例单独一套目录,避免多实例时日志和数据混在一起。实际生产环境中我还建议用systemd来管理Redis实例,写一个简单的service文件,实现开机自启和异常重启。集群模式下,节点的自动拉起很重要,否则主从切换后如果节点一直离线,整个集群的可用性还是受影响。

3. 集群配置与逐节点部署

3.1 redis.conf核心参数逐个拆解

Redis集群的配置并不复杂,但每个参数都值得仔细理解。下面是我在6个节点上使用的配置片段,以节点一为例:

# 节点监听端口 port 7001 bind 0.0.0.0 protected-mode no # 以守护进程方式运行 daemonize yes pidfile /var/run/redis_7001.pid # 持久化配置 appendonly yes appendfilename "appendonly-7001.aof" appendfsync everysec dir /data/redis/data/7001 logfile /data/redis/logs/redis-7001.log # 集群开关,这是最核心的参数 cluster-enabled yes cluster-config-file nodes-7001.conf cluster-node-timeout 5000 # 内存限制与淘汰策略 maxmemory 2gb maxmemory-policy allkeys-lru

这里逐个解释关键参数:

  • cluster-enabled yes:开启集群模式。如果不加这个,redis-cli创建集群时会直接报错提示节点未启用集群。
  • cluster-config-file nodes-7001.conf:节点状态文件。Redis会自动维护这个文件,记录本节点视角下的集群拓扑。千万不要手动编辑它,节点启动时会自动更新它。一旦文件损坏,删掉后重启,它会重新从其他节点同步拓扑。
  • bind 0.0.0.0:监听所有网卡。如果有多网卡,建议只绑定内网IP,避免Redis直接暴露到不安全网络。
  • protected-mode no:在没有密码策略时,受保护模式会拒绝非本机连接,集群节点间通信会失败。这里设为no,再通过防火墙限制访问来源,更安全。
  • maxmemory 2gb:每个节点分配2GB内存。注意集群的容量是所以主节点内存之和,从节点不占用额外分片容量。
  • appendfsync everysec:每秒同步一次AOF,兼顾性能和数据安全。

每个节点根据实际规划把端口、数据目录、日志文件名改成对应的序号,然后就可以启动了。启动命令很简单:

/usr/local/redis/bin/redis-server /etc/redis/7001.conf

3.2 节点启动后的状态自检

启动后第一步不是急着创建集群,而是先确认每个节点正常启动。看日志是最直接的,启动成功后日志末尾会出现:

Ready to accept connections tcp

如果集群开关是开启的,日志里还会出现类似 "Cluster state changed" 的提示。我习惯在创建集群前先跑一次连通性测试,用redis-cli连到每个节点执行ping:

/usr/local/redis/bin/redis-cli -p 7001 ping

返回PONG说明节点正常。接着检查集群状态:

/usr/local/redis/bin/redis-cli -p 7001 cluster info /usr/local/redis/bin/redis-cli -p 7001 cluster nodes

未创建集群时,cluster_state显示为fail,cluster_nodes只有本节点信息,这是正常的。如果cluster nodes能看到其他节点,说明集群已经通过某种方式建立过,需要重新初始化(在测试环境可以直接删除nodes文件后重启)。

另外要注意防火墙配置。Redis集群除了业务使用的主端口,还需要开放集群总线端口,它是主端口+10000。比如7001的集群总线端口是17001,节点间心跳通信走的是这个端口。我第一次部署时只放开了7001-7006,结果创建集群时一直报节点间无法通信,排查半天才发现是总线端口被墙了。

4. 集群创建与常见拓扑验证

4.1 用自带的redis-cli一键创建集群

Redis从5.0开始推荐使用redis-cli --cluster create这条命令创建集群,很直观。在任意一个已经启动的节点上执行:

/usr/local/redis/bin/redis-cli --cluster create \ 192.168.10.11:7001 192.168.10.12:7002 \ 192.168.10.13:7003 192.168.10.14:7004 \ 192.168.10.15:7005 192.168.10.16:7006 \ --cluster-replicas 1

这条命令的逻辑是:前三个节点(7001、7002、7003)被分配为主节点,后三个作为从节点,同时为每个主节点指定一个从节点。--cluster-replicas 1表示每个主节点配1个从节点。命令执行过程中会让你确认槽位分配方案,输入yes即可。

创建完成后,Redis会打印一段摘要,包含每个主节点的槽位范围、从节点归属等信息。我习惯把这段输出留存,后面扩容、迁移时方便对照。

创建完之后,用如下命令检查集群整体状态:

/usr/local/redis/bin/redis-cli -p 7001 cluster info

正常情况下cluster_state显示为ok,cluster_slots_assigned为16384,说明槽位全部覆盖。再看cluster_nodes,可以确认6个节点的角色。主节点的role显示master,从节点role显示slave,并通过master字段指向对应的主节点ID。

有个经验之谈:创建集群时,如果使用的是不同网段的IP,建议统一写成内网IP。如果某台机器配置了多个IP,Redis默认获取第一个,很可能不是你期望的那个,导致其他节点连接失败。

4.2 槽位分布与数据读写验证

集群搭好之后,第一步验证就是写入读取。用redis-cli连接任意节点执行写入命令:

/usr/local/redis/bin/redis-cli -c -p 7001 set hello world

注意一定要加-c选项,也就是cluster模式。admin节点会根据key计算出槽位,如果槽位不在当前节点,-c会自动跳转到正确节点执行命令。如果忘了加-c,会报错:

(error) MOVED 16384 192.168.10.15:7005

这个MOVED重定向是正常的,不是故障。集群客户端会自动处理,但手动测试时容易误判。

多key操作是个值得专门提醒的点。集群模式下,不同key如果映射到不同槽位,一条命令涉及多个key时会出现CROSSSLOT错误:

(error) CROSSSLOT Keys in request don't hash to the same slot

解决办法使用哈希标签(hash tag),让相关key落在同一个槽位。方法是把key写成包含花括号的形式,Redis只对花括号内的部分计算槽位。例如{user}:name和{user}:age,花括号里的user决定了它们都落在同一个槽位。业务代码里如果频繁使用mget、del等多key操作,在设计key时就要有意识地利用哈希标签。

还有一个分片场景下的经典问题:在大数据量时需要知道某个key落在哪个节点,可以用cluster keyslot命令查询:

/usr/local/redis/bin/redis-cli -p 7001 cluster keyslot hello

返回的是槽位编号,然后通过cluster nodes输出对照就能定位到是哪个节点。

4.3 主从切换与故障转移演练

集群部署完成后,高可用到底靠不靠谱,建议做一次故障演练来验证。演练方法很简单:直接停掉一个主节点的进程,观察从节点是否自动提升为新的主节点。

我用7001节点举例。执行:

/usr/local/redis/bin/redis-cli -p 7001 shutdown nosave

等几秒后,查看节点状态:

/usr/local/redis/bin/redis-cli -p 7002 cluster nodes

可以看到7001对应的那个分片原本的从节点状态变成了master,role字段变化,cluster_state依然保持ok。这说明故障转移生效了。等到原7001节点重新启动后,它会作为从节点加入集群,自动从新的主节点同步数据,回到高可用状态。

一个容易被忽略的细节:故障转移的触发条件不只是进程退出。内存超限、长时间阻塞、网络分区等都可能导致节点被判定故障。演练时除了kill进程,还可以试试用防火墙阻断两个节点间的通信,观察网络分区场景下的行为。我之前把集群总线端口断掉后,个别节点进入了fail状态,但主从切换比直接kill进程要慢很多,原因是集群需要先收集足够多的投票才触发转移。

关于投票机制,简单说一下:一个节点被标记为疑似故障(pfail)后,集群中其他主节点会互相交换信息,当超过半数的主节点(过半原则)认为该节点不可达,它才会被判定为fail。这就是为什么集群至少需要3个主节点的原因,2个主节点时如果其中一个挂了,无法形成多数票。

5. 常见问题与排查技巧实录

5.1 集群状态异常的核心排查思路

遇到cluster_state:fail,最常见的两类原因:一是槽位未完整覆盖,二是部分节点失联导致无法形成多数派。排查时按顺序来:

先看cluster_info里的cluster_slots_assigned,如果小于16384,说明有槽位没有分配给任何节点。这种情况通常发生在扩容中途中止或者节点数据迁移异常之后。此时需要找出来哪些槽位未覆盖:

/usr/local/redis/bin/redis-cli -p 7001 cluster slots

对比输出,找到缺失的槽位区间,然后手动迁移槽位,或者重新补充节点。

再看cluster_nodes中的节点状态。如果出现大量disconnected,优先检查网络和防火墙。我遇到过一个典型场景:迁移机房后,节点间网络延迟从0.2ms飙到30ms,cluster-node-timeout还是默认的15000,节点间频繁出现瞬间断连,不断触发主从切换,日志里全是切换记录。最后把timeout调到10000,才稳定下来。

跨机房部署时还要关注cluster-allow-emptyslots参数。默认情况下,如果集群槽位全部被删除或者初始化不完整,集群会拒绝服务,这是安全机制。但如果是在做迁移演练,想先把节点拉起来再慢慢填充槽位,可能需要临时开启这个参数。生产环境不建议长期开启。

5.2 日常运维需要盯紧的几个指标

集群搭起来只是开始,日常运维要盯的指标比单机多不少:

一是cluster_state是否稳定。写成监控脚本,定期查询,一旦变成fail立即告警。二是主从复制偏移量。在从节点执行info replication,观察master_repl_offset和slave_repl_offset是否持续一致,长期不一致说明从节点同步有延迟。三是内存增长趋势。每个节点单独设maxmemory,但要预留buffer,不要等内存满了再扩容。

数据备份策略也要调整思路。集群模式的数据分布在多个节点上,备份时需要对每个节点分别执行BGSAVE或BGREWRITEAOF,然后统一拷贝RDB/AOF文件到一个集中位置。恢复时更麻烦,要么全量恢复所有节点数据再重建集群,要么在单个分片故障时用对应节点的备份文件恢复。我建议每周做一次全量备份,每天的增量靠AOF。备份文件至少保留两周,这是最基本的底线。

日常小操作里还有个容易被忽略的点:redis-cli --cluster reshard做在线扩容时,数据迁移的单位是槽,Redis会以slot为单位把key逐个搬移到新节点。迁移过程中key不会丢失,但迁移期间访问正在迁移的key会多一次ASK重定向,客户端需要支持处理。老牌的客户端库一般都有处理,但如果你自己写了脚本直接用redis-cli,遇到ASK要先请求目标节点,然后再访问,否则会失败。

5.3 我踩过的三个实际坑

第一个坑是透明大页导致的高延迟。部署初期,线上请求p99偶尔会飙到几百毫秒,检查一切正常,后来才反应过来是THP没关闭。Redis官方文档里明确建议禁用THP,否则写时复制时内存和CPU消耗都会异常。修改系统内核参数之后,实测P99恢复了正常波动水平。

第二个坑是节点时间不同步。集群节点间的时间偏差会影响故障检测和选举判断,尤其是多个节点跨越不同物理机时。我部署时没注意集群里6台机器的时间同步偏差到了1秒以上,平时看着没事,故障切换时就会投票失败。后来统一配置了时间同步服务,问题消失。

第三个坑是数据目录写权限。Redis进程通常以普通用户运行,如果数据目录的属主不对,启动时会报Can't open the append-only file: Permission denied。手动启动时可能还好,一旦配置了systemd自动拉起,进程用户变了,目录权限没跟上,就会出现启动即失败然后又重启的循环。检查用户归属和目录权限是每条新机器上线前必须过一遍的。

6. 写在最后的一点运维体会

整套Redis集群部署下来,我的感受是集群本身不难建,难的是理解每个参数背后的原因,以及建立起一套完整的运维习惯。redis-cli --cluster create这条命令一分钟就能建好集群,但真正的挑战在后面的扩容、缩容、故障切换、备份恢复这些日常操作。

生产环境里的集群迁移,我坚持一个原则:先小规模验证,再逐步迁移流量。先在测试环境完整跑一遍扩容和故障切换,记录每一步的输出和影响,再在灰度环境重复一次,最后才动线上。每个节点切流之前,检查数据一致性,确认cluster_state稳定,观察一段时间再继续下一步。

如果你现在正准备部署Redis集群,可以把这篇文章里的配置作为初始模板,但最终一定要根据你的业务请求特征、网络环境、机器配置去调优。特别是cluster-node-timeout、maxmemory-policy这些参数,没有一套放之四海皆准的值,能做的就是把原理搞清楚,然后通过压测和演练找出最适合自己的配置。

最后分享一个小技巧:给每个节点的主目录和日志文件加上清晰命名,比如带上机房、分片和角色信息。这个习惯帮我省过无数次排查时间,看起来是小事,等集群节点多起来,价值就会体现出来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询