1. 为什么要在本地虚拟机里折腾 Redis 集群
很多人第一次听到“Redis 集群”这四个字,第一反应是——这玩意儿不是生产环境才用的吗,我本地学它干嘛。我刚开始也是这么想的,直到有一次帮朋友排查一个缓存雪崩的问题,才发现单机 Redis 和集群 Redis 在行为上差别太大了,比如 key 的分布、槽位的概念、主从切换时的表现,这些你在单机上永远模拟不出来。所以后来我养成了一个习惯:凡是生产上要用的中间件,先在本地虚拟机里搭一套最小可用的集群,把该踩的坑提前踩一遍。
这篇文章就是把我自己搭 Redis 集群的完整过程整理出来,从虚拟机安装到集群跑通,每一步都写清楚为什么这么做。适合的人群很明确:一是刚接触 Redis 集群、想动手但不知道从哪下手的新手;二是平时只用过单机 Redis、想搞清楚集群到底怎么回事的开发者;三是需要本地复现集群问题、但不想动生产环境的运维同学。整套方案我用的是三台虚拟机、每台跑两个 Redis 实例,凑成三主三从的标准集群,这个规模既能体现集群的核心特性,又不会把本地机器压垮。
先说清楚一个前提:本地虚拟机和真实服务器在原理上没区别,只是资源少一点。你在虚拟机里学到的槽位分配、节点握手、主从复制这些知识,搬到真实环境完全通用。所以别觉得本地搭是“玩具”,它就是你理解集群的最好沙盘。
2. 搭建前的整体设计与资源规划
2.1 为什么选三主三从而不是一主一从
Redis 集群有个硬性规定:至少需要三个主节点才能组成集群,因为集群的故障转移需要多数派投票,三个主节点才能容忍一个挂掉。如果你只搞两个主节点,那挂一个整个集群就不可用了,失去了集群的意义。所以主节点数量最少是三个,这是底线。
那从节点呢?从节点负责数据备份和读扩展,理论上每个主节点配一个从节点就够了。我选择三主三从,是因为这个配置能完整演示“主节点挂掉后从节点自动顶上来”这个过程。如果你只搭三个主节点不配从节点,那主节点一挂集群就直接不可用了,你根本看不到故障转移的效果。所以三主三从是学习集群的最小完整配置,再小就缺胳膊少腿了。
2.2 虚拟机数量与资源分配的计算
三主三从一共六个 Redis 实例,那到底开几台虚拟机?有两种方案:一是开六台虚拟机,每台跑一个实例;二是开三台虚拟机,每台跑两个实例。我选后者,原因很实际——本地机器资源有限。每台虚拟机至少要吃 1GB 内存,六台就是 6GB,再加上宿主机本身的开销,8GB 内存的笔记本直接卡死。而三台虚拟机每台 1.5GB 内存,总共 4.5GB,大部分机器都能扛住。
具体资源分配是这样的:每台虚拟机分配 1 到 2 核 CPU、1.5GB 内存、20GB 硬盘。Redis 本身很轻量,空载时一个实例也就占几十 MB 内存,1.5GB 足够跑两个实例还有富余。硬盘 20GB 是因为要装操作系统,实际 Redis 数据占不了多少。网络方面用 NAT 模式就行,虚拟机之间能互通,也能访问外网装软件。
这里有个细节要注意:每台虚拟机上的两个 Redis 实例,端口要错开。我习惯用 7001 和 7002 这种端口号,一看就知道是集群用的。六个实例的端口分别是 7001、7002、7003、7004、7005、7006,分布在三台机器上,每台两个。
2.3 网络与端口规划表
把规划写成表格最清楚,后面配置的时候直接照着填就行:
| 虚拟机 | IP 地址 | 实例端口 | 角色 |
|---|---|---|---|
| 节点一 | 192.168.100.11 | 7001 | 主节点 |
| 节点一 | 192.168.100.11 | 7002 | 从节点 |
| 节点二 | 192.168.100.12 | 7003 | 主节点 |
| 节点二 | 192.168.100.12 | 7004 | 从节点 |
| 节点三 | 192.168.100.13 | 7005 | 主节点 |
| 节点三 | 192.168.100.13 | 7006 | 从节点 |
这个 IP 段是我随便选的私有地址,你用自己习惯的网段就行,只要三台机器在同一个网段能互相 ping 通就可以。端口从 7001 开始是为了避开 Redis 默认的 6379,免得和你机器上可能已有的 Redis 冲突。
提示:规划阶段一定要把这张表定下来,后面所有配置文件都基于它。中途改 IP 或端口会让你想把键盘砸了,别问我怎么知道的。
3. 虚拟机环境准备与系统配置
3.1 虚拟机软件的安装与基础设置
虚拟机软件我用的是 VirtualBox,免费开源,功能够用。VMware 也行,看你习惯。安装过程就是下一步下一步,没什么好说的。装完之后新建虚拟机,选择 Linux 系统,内存给 1.5GB,硬盘给 20GB,网络选 NAT 模式。
操作系统我选的是 CentOS 7,原因很简单——稳定、资料多、命令熟悉。Ubuntu 也可以,但 CentOS 的 systemctl 和防火墙配置我更顺手。安装系统的时候记得勾选“基础设施服务器”或者“最小安装”,不需要图形界面,省资源。安装过程中设置好 root 密码,创建一个普通用户也行,但我习惯直接用 root 操作,本地学习环境不用太讲究权限。
系统装完之后第一件事是配置静态 IP。默认的 DHCP 每次重启可能变 IP,集群配置里写死了 IP,一变就全乱套。配置静态 IP 的方法是修改网卡配置文件,通常在/etc/sysconfig/network-scripts/ifcfg-ens33这个路径下,具体文件名看你的网卡名。把 BOOTPROTO 改成 static,然后加上 IPADDR、NETMASK、GATEWAY、DNS1 这几项。
# 编辑网卡配置 vi /etc/sysconfig/network-scripts/ifcfg-ens33 # 修改以下内容 BOOTPROTO=static ONBOOT=yes IPADDR=192.168.100.11 NETMASK=255.255.255.0 GATEWAY=192.168.100.1 DNS1=114.114.114.114改完之后重启网络服务:systemctl restart network。然后用ip addr确认 IP 生效。三台机器都这么配,IP 分别设成 11、12、13。
3.2 关闭防火墙与 SELinux 的取舍
这一步是新手最容易卡住的地方。Redis 集群节点之间要互相通信,涉及多个端口,如果防火墙开着,节点握手会失败,集群建不起来。本地学习环境我建议直接把防火墙关了,省去配规则的麻烦。
# 停止防火墙 systemctl stop firewalld # 禁止开机自启 systemctl disable firewalldSELinux 也要关,它有时候会拦截 Redis 的文件访问。修改/etc/selinux/config,把 SELINUX 改成 disabled,然后重启机器生效。
注意:关防火墙和 SELinux 只在本地学习环境这么做。生产环境必须配置精确的防火墙规则,只放行必要的端口,SELinux 也要用正确的策略而不是直接关掉。这个区别心里要有数。
3.3 主机名与 hosts 映射配置
给三台机器起个好记的主机名,后面操作的时候不容易搞混。我用 node1、node2、node3 这种命名。
# 在节点一上执行 hostnamectl set-hostname node1 # 节点二 hostnamectl set-hostname node2 # 节点三 hostnamectl set-hostname node3然后在每台机器的/etc/hosts文件里加上三台机器的映射,这样后面可以用主机名代替 IP,配置看起来清爽一些。
192.168.100.11 node1 192.168.100.12 node2 192.168.100.13 node3配完之后互相 ping 一下,确保三台机器网络互通。ping node2和ping node3都能通,说明网络没问题,可以进入下一步。
4. Redis 安装与多实例配置详解
4.1 Redis 源码编译安装的完整步骤
Redis 的安装有两种方式:包管理器安装和源码编译安装。我强烈建议用源码编译,原因有两个:一是包管理器装的版本往往比较老,集群功能可能有 bug;二是源码安装你能控制安装路径,多实例部署时更灵活。
先装编译需要的依赖:
yum install -y gcc make wget然后下载 Redis 源码。版本我选的是 6.2 系列,这个版本集群功能很稳定,资料也多。下载地址去 Redis 官网找最新的稳定版就行。
cd /opt wget https://download.redis.io/releases/redis-6.2.7.tar.gz tar -zxvf redis-6.2.7.tar.gz cd redis-6.2.7编译和安装:
make make install PREFIX=/usr/local/redismake这一步如果报错,大概率是 gcc 版本太低。CentOS 7 默认的 gcc 是 4.8,编译 Redis 6 需要 gcc 5 以上。解决办法是升级 gcc,或者用 Redis 5 的版本。我遇到过这个问题,升级 gcc 的命令比较长,这里给出来:
yum install -y centos-release-scl yum install -y devtoolset-9-gcc devtoolset-9-gcc-c++ scl enable devtoolset-9 bash执行完这几条再make就能过了。编译完成后,Redis 的可执行文件在/usr/local/redis/bin目录下,包括 redis-server、redis-cli、redis-trib.rb 等。
4.2 创建多实例目录结构
一台机器上跑两个实例,目录要分开,不然日志和数据会混在一起。我在/usr/local/redis下建两个目录,分别叫 7001 和 7002。
mkdir -p /usr/local/redis/7001 mkdir -p /usr/local/redis/7002 mkdir -p /usr/local/redis/7001/data mkdir -p /usr/local/redis/7002/data mkdir -p /usr/local/redis/7001/logs mkdir -p /usr/local/redis/7002/logs每个实例目录下放一个 redis.conf 配置文件,从源码目录复制一份过来改。这样每个实例有独立的配置、独立的数据文件、独立的日志,互不干扰。
4.3 集群模式配置文件逐项解读
集群模式的配置和单机模式差别不小,我逐项说明关键配置,你照着改就行。
# 端口,每个实例不同 port 7001 # 绑定地址,0.0.0.0 表示监听所有网卡 bind 0.0.0.0 # 关闭保护模式,否则外部连不上 protected-mode no # 后台运行 daemonize yes # 日志文件路径 logfile "/usr/local/redis/7001/logs/redis.log" # 数据文件目录 dir /usr/local/redis/7001/data # 开启集群模式,这是最关键的 cluster-enabled yes # 集群配置文件,由 Redis 自动维护 cluster-config-file nodes-7001.conf # 节点超时时间,单位毫秒 cluster-node-timeout 15000 # 开启 AOF 持久化 appendonly yes # 设置密码(可选,但建议设) requirepass yourpassword masterauth yourpassword这里有几个点要重点解释。cluster-enabled yes是开启集群模式的开关,不写这个就是单机模式。cluster-config-file指定的文件 Redis 会自动生成和维护,你不需要手动创建,但要知道它在哪,出问题的时候要看这个文件。cluster-node-timeout是节点失联的超时时间,设太短会误判节点下线,设太长故障转移慢,15 秒是个比较稳妥的值。
密码这块要注意,requirepass是客户端连接密码,masterauth是从节点连主节点用的密码,两个都要设成一样的,否则主从复制会失败。如果你不想设密码,这两行都注释掉,但集群模式下不设密码其实有点危险,本地学习也建议设上,养成好习惯。
4.4 批量生成配置文件的技巧
六个实例的配置文件内容大同小异,只有端口、目录、文件名不同。手动改六遍太蠢了,我写了个简单的脚本批量生成。
#!/bin/bash # 在每台机器上执行,生成两个实例的配置 for port in 7001 7002; do mkdir -p /usr/local/redis/$port/data mkdir -p /usr/local/redis/$port/logs cat > /usr/local/redis/$port/redis.conf << EOF port $port bind 0.0.0.0 protected-mode no daemonize yes logfile "/usr/local/redis/$port/logs/redis.log" dir /usr/local/redis/$port/data cluster-enabled yes cluster-config-file nodes-$port.conf cluster-node-timeout 15000 appendonly yes requirepass yourpassword masterauth yourpassword EOF done这个脚本在每台机器上跑一遍,就会生成两个实例的配置。注意端口那行要根据机器改,节点一用 7001 和 7002,节点二用 7003 和 7004,节点三用 7005 和 7006。脚本里的yourpassword换成你自己的密码。
实操心得:配置文件里的路径一定要用绝对路径,用相对路径的话,Redis 启动时的工作目录不确定,日志和数据可能跑到奇怪的地方去。这个坑我踩过,找了半天日志。
5. 启动实例与集群创建全流程
5.1 逐个启动 Redis 实例并验证
配置写好后,用每个实例自己的配置文件启动 Redis:
/usr/local/redis/bin/redis-server /usr/local/redis/7001/redis.conf /usr/local/redis/bin/redis-server /usr/local/redis/7002/redis.conf三台机器都执行对应的启动命令,六个实例全部拉起来。启动后验证一下:
ps -ef | grep redis应该能看到六个 redis-server 进程。再用客户端连一下:
/usr/local/redis/bin/redis-cli -p 7001 -a yourpassword连上之后执行cluster info,如果看到cluster_state:fail,这是正常的,因为集群还没创建。执行cluster nodes会看到只有自己一个节点。这说明实例启动成功了,只是还没组集群。
5.2 使用 redis-cli 创建集群的命令拆解
Redis 5 之后,集群创建工具集成到了 redis-cli 里,不用再单独装 ruby 环境跑 redis-trib.rb 了,方便很多。创建集群的命令在任意一台机器上执行一次就行:
/usr/local/redis/bin/redis-cli -a yourpassword --cluster create \ 192.168.100.11:7001 192.168.100.11:7002 \ 192.168.100.12:7003 192.168.100.12:7004 \ 192.168.100.13:7005 192.168.100.13:7006 \ --cluster-replicas 1这个命令有几个关键点。--cluster create表示创建集群,后面跟六个节点的地址。--cluster-replicas 1表示每个主节点配一个从节点,Redis 会自动把后三个节点配成前三个的从节点。执行后会有一个确认提示,问你是否接受这个分配方案,输入 yes 回车。
然后你会看到一堆输出,显示节点握手、槽位分配、主从关系建立的过程。最后出现[OK] All 16384 slots covered就说明集群创建成功了。16384 是 Redis 集群的槽位总数,所有槽位都被覆盖意味着集群状态正常。
5.3 集群状态验证的三种方法
集群建好后要验证,我一般用三种方法交叉确认。
第一种是cluster info,看集群整体状态:
redis-cli -p 7001 -a yourpassword cluster info关注这几个字段:cluster_state:ok表示集群正常,cluster_slots_assigned:16384表示槽位全部分配,cluster_known_nodes:6表示六个节点都认识。
第二种是cluster nodes,看节点详情:
redis-cli -p 7001 -a yourpassword cluster nodes输出里每一行是一个节点,能看到节点 ID、IP 端口、角色(master 或 slave)、负责的槽位范围、主从关系。确认三个 master 各自负责一段槽位,三个 slave 分别对应三个 master。
第三种是实际读写测试。集群模式下连接要加-c参数,否则操作跨槽位的 key 会报错:
redis-cli -c -p 7001 -a yourpassword set name "test" get name如果 set 和 get 都成功,说明集群读写正常。你可以多设几个不同的 key,观察客户端自动重定向到不同节点的过程,这就是集群的分片效果。
6. 集群常见故障与排查实录
6.1 集群创建失败的典型原因
新手搭集群最容易卡在创建这一步,我整理了几个高频原因。
第一个是节点之间网络不通。集群创建要求所有节点能互相通信,如果某台机器防火墙没关或者 IP 配错,握手就会失败。排查方法是逐个 ping,再用telnet IP 端口测试端口连通性。
第二个是配置文件里cluster-enabled没开或者写错。这个错误很隐蔽,因为实例能正常启动,但创建集群时会报“节点不是集群模式”。检查方法是用redis-cli连上去执行cluster info,如果报错说命令不支持,就是集群模式没开。
第三个是密码不一致。requirepass和masterauth必须一致,而且所有节点的密码要相同。如果密码不对,节点握手时会认证失败。排查方法是看日志,日志里会有认证相关的错误信息。
第四个是节点上有残留的集群数据。如果你之前创建过集群失败了,nodes-7001.conf文件里可能残留了旧信息,导致新集群创建异常。解决办法是停掉所有实例,删掉所有nodes-*.conf文件和 data 目录下的数据,重新启动再创建。
6.2 槽位分配异常的修复方法
有时候集群创建成功了,但cluster info显示cluster_state:fail,或者槽位没有全部分配。这种情况通常是某个节点没起来或者握手失败。
修复方法是手动分配槽位。先用cluster nodes找到哪些槽位没分配,然后用cluster addslots命令补上。不过手动分配槽位很麻烦,16384 个槽位要一个个算。更简单的办法是删掉集群重建,把残留文件清干净,重新执行创建命令。
还有一种情况是槽位分配了但状态还是 fail,这通常是主从关系没建立好。检查cluster nodes输出,看 slave 有没有正确指向 master。如果 slave 显示的状态是fail或者disconnected,说明主从复制有问题,重点检查masterauth配置和网络连通性。
6.3 主从切换的模拟与观察
集群搭好了,不模拟一次故障转移就白搭了。我教你一个安全的模拟方法:手动停掉一个主节点,观察从节点是否自动顶上。
先找到节点一上的主节点是哪个端口,假设是 7001。在节点一上停掉 7001:
redis-cli -p 7001 -a yourpassword shutdown然后立刻在另一个节点上观察集群状态:
redis-cli -p 7003 -a yourpassword cluster nodes你会看到 7001 的状态变成 fail,而它对应的从节点(假设是 7004)会变成 master,接管 7001 负责的槽位。这个过程通常在几秒到十几秒内完成,取决于cluster-node-timeout的设置。
等确认故障转移完成后,把 7001 重新启动:
/usr/local/redis/bin/redis-server /usr/local/redis/7001/redis.conf重启后 7001 会自动以从节点身份加入集群,变成新主节点的从节点。这个“主变从、从变主”的过程就是集群高可用的核心,你在本地多模拟几次,对集群的理解会深刻很多。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决办法 |
|---|---|---|---|
| 集群创建报“节点不是集群模式” | cluster-enabled 未开启 | cluster info 报错 | 修改配置重启实例 |
| 节点握手失败 | 网络不通或防火墙未关 | ping 和 telnet 测试 | 关防火墙、检查 IP |
| 认证失败 | 密码不一致 | 查看日志认证错误 | 统一所有节点密码 |
| 槽位未全部分配 | 节点未全部启动 | cluster nodes 查看 | 启动缺失节点或重建集群 |
| 主从复制断开 | masterauth 未配置 | 查看 slave 状态 | 补上 masterauth 配置 |
| 集群状态 fail | 多数主节点不可用 | cluster info 查看 | 恢复节点或重建集群 |
避坑技巧:每次改完配置重启实例后,先别急着建集群,用
cluster info确认实例是集群模式,用cluster nodes确认没有残留的旧节点信息。这两步花不了一分钟,能省掉后面半小时的排查。
7. 集群使用中的实操经验与性能观察
7.1 客户端连接集群的正确姿势
集群模式下,普通客户端连接方式是不行的,必须用支持集群的客户端。命令行用redis-cli -c,这个-c参数开启自动重定向,当你操作的 key 不在当前节点时,客户端会自动转发到正确的节点。
编程语言里的客户端也要用集群模式。比如 Java 的 Jedis 要用JedisCluster类,Python 的 redis-py 要用RedisCluster类。用单机客户端连集群,跨槽位的操作会直接报错,这个坑很多人踩过。
还有一个细节:集群模式下不支持多 key 操作跨槽位。比如mget key1 key2,如果 key1 和 key2 在不同的槽位,这个命令会失败。解决办法是用 hash tag,把相关的 key 用{}包起来,强制它们落在同一个槽位,比如mget {user}:1 {user}:2,这样就能正常执行。
7.2 槽位与 key 分布的观察方法
想直观感受集群的分片效果,可以用cluster keyslot命令查看某个 key 落在哪个槽位:
redis-cli -p 7001 -a yourpassword cluster keyslot name返回一个 0 到 16383 之间的数字,这就是 key 的槽位。然后你可以用cluster nodes看这个槽位属于哪个节点。多试几个 key,你会发现不同的 key 分布在不同节点上,这就是集群的负载均衡。
如果你想让某些 key 落在同一个节点,用 hash tag 就行。cluster keyslot {user}:1和cluster keyslot {user}:2会返回相同的槽位,因为它们只计算{user}这部分。
7.3 本地集群的性能边界认知
本地虚拟机搭的集群,性能肯定比不上真实服务器,但用来学习足够了。我实测下来,三台虚拟机每台 1.5GB 内存的配置,集群读写性能大概在几千 QPS 这个量级,延迟在毫秒级。这个数据没有参考价值,因为受宿主机性能影响太大,但能让你对集群的吞吐有个感性认识。
真正要关注的是集群的行为特性,而不是绝对性能。比如主从复制的延迟、故障转移的时间、槽位迁移的过程,这些在本地和生产的表现是一致的。你把本地集群玩熟了,到生产环境排查问题就有底气。
个人体会:本地集群最大的价值是让你敢动手。生产环境的集群你不敢随便停节点、不敢随便改配置,但本地集群你可以随便折腾,停掉主节点看故障转移,删掉数据重建集群,这些操作做多了,集群的原理就刻在脑子里了。
7.4 关闭与重启集群的规范操作
学习完了要关机器,别直接关机,规范操作是先停 Redis 实例再关虚拟机。停实例的命令:
redis-cli -p 7001 -a yourpassword shutdown六个实例都停掉之后再关虚拟机。下次要用的时候,先开虚拟机,再逐个启动实例,集群会自动恢复,不需要重新创建。因为集群的元数据保存在nodes-*.conf文件里,只要这些文件在,集群信息就不会丢。
如果重启后集群状态是 fail,先别慌,等一会儿再看。集群节点重新握手需要时间,通常几十秒内会恢复正常。如果一直 fail,检查是不是有节点没启动,或者 IP 变了导致节点找不到彼此。
8. 从本地集群到真实场景的迁移思路
本地集群跑通之后,你可能会想:这套东西怎么用到真实项目里?其实核心概念完全一样,只是规模和配置不同。真实环境可能是一个主节点配两个从节点,跨机房部署,有专门的监控和告警。但你在本地学到的槽位分配、主从复制、故障转移这些原理,是通用的。
迁移的时候要注意几个差异点。一是真实环境的网络延迟比本地高,cluster-node-timeout要适当调大,否则容易误判节点下线。二是真实环境的数据量大,要考虑槽位迁移的性能影响,通常选在业务低峰期做。三是真实环境的安全要求高,密码要复杂,防火墙要精确配置,不能像本地这样直接关掉。
我个人的建议是,本地集群用来学习和验证方案,真实环境部署前一定要在测试环境再跑一遍,把配置参数根据实际情况调优。本地集群的价值在于让你理解原理、熟悉操作,而不是直接照搬配置。
最后分享一个我常用的技巧:把集群的启动、停止、状态检查写成脚本,放在每台机器上。这样每次开机不用敲一堆命令,执行一个脚本就搞定。脚本内容很简单,就是循环启动实例、循环检查状态,但能省不少事。学习阶段多动手是好事,但重复劳动能省则省,把精力留给真正需要思考的地方。