1. 为什么要在本地虚拟机里折腾Redis集群很多人第一次接触Redis集群都是在云服务商那里买一个现成的实例点几下鼠标就完事了。但真到了要理解数据分片、主从切换、槽位分配这些机制的时候光看文档是看不明白的。我自己当初也是这样直到有一次线上环境出现节点宕机槽位迁移卡住我才意识到必须亲手从零搭一遍才能把整个链路吃透。本地虚拟机部署Redis集群最大的好处是可以随便折腾。你可以模拟节点掉线、网络分区、主从切换甚至直接把某个节点的数据目录删掉看它怎么恢复。这些操作在线上环境是想都不敢想的。而且虚拟机可以打快照搞崩了一键回滚成本几乎为零。这篇文章面向的是完全没有集群经验的新手但需要你至少用过单机版Redis知道redis-cli怎么连、SET和GET怎么用。如果你连单机都没装过建议先花半小时把单机跑起来再来看这篇。我会从虚拟机创建开始一步步带你搭出一个三主三从的Redis集群每个步骤都会解释为什么这么做以及我踩过的坑。整个方案的核心思路是用一台物理机开三个虚拟机每个虚拟机跑两个Redis实例一主一从通过Redis Cluster原生集群模式组网。这样既节省资源又能完整模拟生产环境的多节点拓扑。下面进入实操。2. 虚拟机环境准备与网络规划2.1 虚拟机软件选型与资源分配虚拟机软件我用的是VirtualBox免费开源跨平台支持好快照功能稳定。VMware Workstation也行但个人版授权问题比较麻烦新手直接用VirtualBox省心。下载安装过程就不赘述了官网直接下最新版即可。资源分配上我建议每个虚拟机给2核CPU、2GB内存、20GB硬盘。三个虚拟机加起来就是6核6GB现在随便一台笔记本都能扛住。如果你物理机内存只有8GB可以把每个虚拟机降到1.5GB但不要再低了否则Redis启动时容易因为内存不足被系统杀掉。操作系统选CentOS 7或者Ubuntu 20.04都行我用的是CentOS 7因为生产环境用得比较多命令和路径比较熟悉。安装系统时注意两点一是网络模式选桥接这样三个虚拟机各自有独立IP互相能通物理机也能直接访问二是安装时勾选开发工具省得后面还要补装gcc、make这些编译依赖。提示如果你物理机的网络环境不支持桥接比如公司网络限制了MAC地址可以改用NAT网络加端口转发但配置会麻烦一些新手建议先试试桥接能不能用。2.2 三台虚拟机的IP规划与主机名设置三台虚拟机我分别命名为redis-node1、redis-node2、redis-node3IP规划如下主机名IP地址角色redis-node1192.168.1.101主节点1 从节点2redis-node2192.168.1.102主节点2 从节点3redis-node3192.168.1.103主节点3 从节点1每台机器跑两个Redis实例端口分别是7001和7002。这样三台机器一共六个实例组成三主三从的集群。为什么这么设计因为Redis Cluster要求至少三个主节点才能保证高可用而每个主节点配一个从节点总共六个实例是最小生产级配置。设置主机名用hostnamectl set-hostname redis-node1然后编辑/etc/hosts文件把三台机器的IP和主机名对应关系写进去。这一步很重要后面集群节点互相通信时会用到主机名如果DNS解析不了会报错。# 在每台机器上执行 hostnamectl set-hostname redis-node1 cat /etc/hosts EOF 192.168.1.101 redis-node1 192.168.1.102 redis-node2 192.168.1.103 redis-node3 EOF2.3 关闭防火墙与SELinux的取舍新手最容易卡住的地方就是防火墙。Redis Cluster节点之间需要互相通信涉及集群总线端口默认是数据端口10000比如7001实例的总线端口是17001。如果防火墙没放行这些端口集群创建时会一直卡在Waiting for the cluster to join。我建议在实验环境直接关闭防火墙和SELinux省去排查端口的时间。生产环境当然不能这么干但本地虚拟机里怎么方便怎么来。systemctl stop firewalld systemctl disable firewalld setenforce 0 sed -i s/SELINUXenforcing/SELINUXdisabled/g /etc/selinux/config关掉之后用getenforce确认返回Permissive或Disabled。这一步不做的话后面redis-cli --cluster create命令大概率会失败而且报错信息很不直观新手根本想不到是SELinux在作怪。3. Redis安装与六实例配置细节3.1 编译安装Redis 6.x的完整流程CentOS 7自带的yum源里Redis版本太老不支持集群模式的一些新特性所以我选择源码编译安装Redis 6.2。先装依赖yum install -y gcc make wget然后下载、解压、编译cd /usr/local/src wget https://download.redis.io/releases/redis-6.2.6.tar.gz tar -zxvf redis-6.2.6.tar.gz cd redis-6.2.6 make MALLOClibc make install PREFIX/usr/local/redis这里有个细节make的时候加MALLOClibc是为了避免jemalloc内存分配器在某些系统上编译报错。make install指定PREFIX是为了把可执行文件统一放到/usr/local/redis/bin下方便管理。编译完成后把源码目录里的redis.conf复制到/usr/local/redis/下作为配置模板cp /usr/local/src/redis-6.2.6/redis.conf /usr/local/redis/3.2 每个实例的配置文件该怎么写六个实例需要六份配置文件分别放在/usr/local/redis/cluster/7001/redis.conf、/usr/local/redis/cluster/7002/redis.conf等目录下。我以7001为例列出关键配置项port 7001 bind 0.0.0.0 daemonize yes pidfile /var/run/redis_7001.pid logfile /usr/local/redis/cluster/7001/redis.log dir /usr/local/redis/cluster/7001 cluster-enabled yes cluster-config-file nodes-7001.conf cluster-node-timeout 15000 appendonly yes protected-mode no逐项解释一下cluster-enabled yes开启集群模式这是最核心的开关。cluster-config-file集群节点信息文件Redis自动维护不要手动改。cluster-node-timeout 15000节点超时时间15秒超过这个时间没响应就认为节点挂了。本地虚拟机网络延迟低15秒足够。appendonly yes开启AOF持久化集群模式下建议开启方便数据恢复。protected-mode no关闭保护模式否则外部IP连不上。7002实例的配置只需要把7001全部替换成7002即可。三台机器上的六个实例都按这个模板来注意dir目录要提前创建好。3.3 启动六个实例并验证端口监听配置文件写好后逐个启动/usr/local/redis/bin/redis-server /usr/local/redis/cluster/7001/redis.conf /usr/local/redis/bin/redis-server /usr/local/redis/cluster/7002/redis.conf三台机器都启动完用ps -ef | grep redis应该能看到六个进程。再用netstat -tlnp | grep 700检查端口监听情况正常应该看到7001、7002、17001、17002四个端口在LISTEN状态。注意如果某个实例启动后立刻退出先看日志文件。最常见的原因是dir目录不存在或者没有写权限其次是端口被占用。日志里会写得很清楚别瞎猜。4. 集群创建与槽位分配实战4.1 redis-cli --cluster create命令的参数逻辑六个实例都跑起来之后就可以创建集群了。命令如下/usr/local/redis/bin/redis-cli --cluster create \ 192.168.1.101:7001 192.168.1.101:7002 \ 192.168.1.102:7001 192.168.1.102:7002 \ 192.168.1.103:7001 192.168.1.103:7002 \ --cluster-replicas 1这个命令的参数顺序有讲究前三个是主节点后三个是从节点。--cluster-replicas 1表示每个主节点配一个从节点。Redis会自动把后三个分配给前三个作为从节点分配规则是尽量让主从不在同一台机器上保证一台机器挂了不会同时丢失主从。执行后会显示一个分配方案问你是否接受。输入yes回车集群开始创建。如果一切顺利你会看到All 16384 slots covered的提示说明16384个槽位已经全部分配完毕。4.2 槽位分配不均时的手动调整有时候自动分配会出现槽位不均衡的情况比如某个主节点分了6000个槽另一个只分了4000个。虽然不影响使用但数据分布会倾斜。可以用reshard命令手动迁移/usr/local/redis/bin/redis-cli --cluster reshard 192.168.1.101:7001命令会交互式地问你迁移多少个槽、目标节点ID是什么、源节点是哪些。这里需要提前用cluster nodes命令查好节点ID。迁移过程中槽位会逐个转移数据也会跟着走线上环境操作时要注意在低峰期进行。4.3 集群状态检查的五个关键命令集群建好后用以下命令验证状态# 查看集群整体状态 redis-cli -c -p 7001 cluster info # 查看节点列表和主从关系 redis-cli -c -p 7001 cluster nodes # 查看槽位分配 redis-cli -c -p 7001 cluster slots # 测试数据读写 redis-cli -c -p 7001 set foo bar redis-cli -c -p 7001 get foo # 查看指定key在哪个槽 redis-cli -c -p 7001 cluster keyslot foocluster info返回的cluster_state:ok表示集群健康。cluster nodes会列出所有节点master和slave标识很清楚。注意-c参数必须加否则redis-cli不会自动重定向到正确的节点遇到跨槽的key会报MOVED错误。5. 主从切换与故障模拟验证5.1 手动kill主节点观察自动切换集群搭好了不验证等于白搭。最直接的验证方式就是杀掉一个主节点看从节点能不能自动顶上。假设101:7001是主节点直接kill -9掉它的进程kill -9 $(cat /var/run/redis_7001.pid)等15秒左右cluster-node-timeout的配置值再执行cluster nodes你会看到原来的从节点变成了master而挂掉的节点显示fail。这时候用redis-cli -c去读写数据应该完全不受影响集群会自动把请求路由到新的主节点。5.2 原主节点恢复后的角色变化把刚才杀掉的实例重新启动/usr/local/redis/bin/redis-server /usr/local/redis/cluster/7001/redis.conf启动后它不会自动变回主节点而是变成新主节点的从节点。这是Redis Cluster的设计逻辑故障切换后原主节点降级为从避免脑裂。你可以用cluster nodes确认它的角色是slave。如果想让它重新变回主节点可以手动执行cluster failover命令但一般没必要保持现状就行。5.3 集群不可用的边界条件测试Redis Cluster有一个重要参数cluster-require-full-coverage默认是yes意思是只要有一个槽位不可用整个集群就拒绝服务。你可以测试一下把某个主节点和它的从节点同时杀掉然后尝试读写会发现报CLUSTERDOWN错误。这个机制保证了数据一致性但可用性会降低。生产环境可以根据业务容忍度决定是否改成no但新手实验阶段建议保持默认先理解这个行为。6. 新手最容易踩的五个坑6.1 配置文件里的bind和protected-mode冲突很多人照着网上教程改配置把bind 127.0.0.1改成bind 0.0.0.0但忘了改protected-mode no结果外部连不上。这两个配置是配合使用的bind 0.0.0.0让Redis监听所有网卡protected-mode no关闭保护模式允许外部访问。少改一个都不行。6.2 集群创建时忘记加-c参数导致MOVED错误用redis-cli操作集群时如果不加-c客户端不会跟随重定向。比如你连的是7001但key实际在7002上就会返回MOVED 1234 192.168.1.101:7002。新手看到这个错误容易懵其实加上-c就自动跳转了。6.3 节点ID混淆导致reshard失败reshard命令需要输入节点ID而cluster nodes输出的第一列就是节点ID是一长串十六进制字符。复制的时候容易多复制或少复制字符导致命令报Unknown node。建议先把ID复制到文本编辑器里核对长度通常是40个字符。6.4 虚拟机快照恢复后集群状态异常用快照恢复虚拟机后集群的nodes-*.conf文件里记录的节点信息可能和实际不一致导致集群状态变成fail。解决办法是恢复快照后把所有节点的nodes-*.conf删掉然后重新创建集群。虽然麻烦但比一个个排查节点状态快得多。6.5 内存不足导致Redis被OOM Killer杀掉虚拟机内存给太小的时候Redis进程可能被系统OOM Killer干掉。查看/var/log/messages会看到Out of memory: Kill process的记录。解决办法是给虚拟机加内存或者在Redis配置里设置maxmemory限制并配置maxmemory-policy淘汰策略。7. 集群日常维护的实用命令7.1 动态添加新节点集群跑起来之后要扩容先启动新实例然后用cluster meet命令加入集群redis-cli -c -p 7001 cluster meet 192.168.1.104 7001加入后新节点是master角色但没有任何槽位。再用reshard命令把部分槽位迁移过去新节点就开始分担数据了。7.2 安全下线一个节点下线节点不能直接kill要先把槽位迁走。用reshard把该节点的槽全部迁到其他节点然后用cluster forget命令让集群忘记它redis-cli -c -p 7001 cluster forget node-id注意forget命令需要在所有节点上执行否则集群状态会不一致。7.3 备份与恢复集群数据集群模式下每个节点都有自己的数据文件备份时要逐个节点备份。恢复时先恢复数据文件再启动实例集群会自动重新识别。如果节点ID变了比如重装系统需要重新加入集群。我个人习惯是每天凌晨用脚本把六个节点的dump.rdb和appendonly.aof打包传到备份服务器保留最近七天的版本。这个习惯在一次误操作后救了我当时不小心FLUSHALL了一个主节点直接从备份恢复损失控制在一天以内。8. 从单机到集群的思维转变搭完这个集群最大的收获不是学会了几个命令而是理解了分布式系统的取舍。单机Redis你只需要关心内存够不够、QPS高不高集群模式下你要考虑槽位分布、网络分区、脑裂风险、数据一致性。每一个参数背后都是可用性和一致性的权衡。比如cluster-node-timeout设得太短网络抖动就会触发主从切换造成不必要的抖动设得太长真挂了半天切不过来。15秒是我在本地环境试出来的经验值生产环境要根据实际网络质量调整。再比如cluster-require-full-coverage默认的yes保证了强一致性但牺牲了部分可用性。如果你的业务能容忍短暂的数据不一致改成no可以让集群在部分槽位不可用时继续提供服务。这个决策没有标准答案取决于业务场景。最后说一个我自己的教训不要在生产环境直接照搬本地虚拟机的配置。本地虚拟机网络延迟低、负载轻很多参数用默认值就能跑。生产环境的网络延迟、磁盘IO、内存压力完全不同必须根据监控数据逐步调优。本地集群的价值在于让你理解机制而不是给你一套可以直接上线的配置。
阅读完成 · 觉得有帮助?