
最近折腾了一套 CentOS 7 环境下的 Redis 部署从单机到主从、哨兵再到正式的集群模式完整走了一遍。网络上关于 Redis 单机安装的教程不少但往往只写到“能启动”就结束了而哨兵和集群相关的内容要么版本偏老要么配置项解释不到位照着抄很容易在验证故障转移的时候发现根本不起作用。这篇文章我打算把三种模式的搭建过程拆开来讲重点放在配置项为什么这么设、节点之间是怎么通信的、遇到坑之后如何定位希望能给同样在 CentOS 7 上做部署的人省点时间。先说读者画像如果你已经知道 Redis 是什么、知道它的是 key-value 数据库但没独立搭过哨兵或集群或者你搭过但被各种超时、脑裂、槽位分配问题折磨过那这篇文章会比较适合你。本文所有操作都在 CentOS 7 环境验证过涉及的关键命令和配置会完整给出。1. 为什么在 CentOS 7 上搭 Redis 这么容易翻车版本与安装方式的选择1.1 环境准备CentOS 7 的系统基线CentOS 7 虽然已经进入维护周期尾声但大量存量业务的线下服务器还在用它。内核 3.10、默认 gcc 4.8.5这是两个影响 Redis 部署的硬性条件。Redis 6.0 之后的部分特性比如多线程 IO 的某些优化路径对新环境更友好但在 CentOS 7 默认工具链下编译 Redis 6.2/7.x 也没有原则性障碍只是要注意别用默认的 yum 源直接装。开始之前建议先做三件事# 1. 确认系统版本和位数 cat /etc/redhat-release # 2. 确认基础编译工具是否存在 yum install -y gcc gcc-c make tcl # 3. 关掉或确认防火墙和 SELinux 状态生产环境请按安全策略操作 systemctl status firewalld getenforcegcc 版本如果过低编译 Redis 时会出现 undefined reference 之类的报错。CentOS 7 自带的 gcc 4.8.5 在某些场景下需要升级到 gcc 5.3 以上才能顺利编译 Redis 6.2 及以上版本。我用过两种方案一是用yum install centos-release-scl再装devtoolset-7二是直接用低版本 Redis 5.x。后者省事但哨兵和集群的运维体验差距不小建议还是升级工具链。1.2 安装方式对比源码编译、默认 yum 源、官方源这是第一个容易翻车的地方。CentOS 7 默认的 EPEL 源里 Redis 版本停留在 3.2.x这个版本可以用但对后面主从复制的部分参数支持不够好更重要的是它和官方文档里的配置习惯差异较大。我实测下来replicaof指令在 3.2 里只是部分兼容很多教程直接复制 5.x 的配置过来启动时就报错。三种方式对比如下安装方式版本优点缺点适用场景yum install redis3.2.x旧安装最快版本老、参数兼容差不适合哨兵/集群实践官方 Software Collections 源5.x相对新、依赖少配置稍麻烦、源可能较慢可以接受源码编译6.2/7.x 任意版本可控性最高需要编译工具、耗时推荐长期实践我的选择是源码编译。原因很实际本文要覆盖单机、哨兵、集群三种模式用同一套二进制文件切换不同配置排查问题时可以避免“是不是版本差异导致”的干扰。源码包从某个镜像站或官方下载都行记得下载对应版本的 tar.gz 压缩包并核对 SHA256 校验值。# 下载后解压这里以 6.2.x 为例 tar -zxvf redis-6.2.x.tar.gz cd redis-6.2.x make -j4 make install PREFIX/usr/local/redismake install之后redis-server、redis-cli、redis-sentinel都会安装到/usr/local/redis/bin目录下。我习惯把这个目录软链到/usr/local/bin后面写 systemd 脚本时路径更干净。提示编译报错jemalloc相关时可以尝试make MALLOClibc这是用系统自带的 libc 内存分配器代替 jemalloc牺牲少量性能换取编译兼容性。线上环境还是建议解决 jemalloc 问题内存碎片表现更好。2. 单机部署从最小配置到 systemd 自启动2.1 redis.conf 核心参数逐段拆解单机模式是最简单的但也是后面所有模式的基础配置文件必须逐行理解否则后面加一行replicaof就可能是灾难。我习惯把配置文件放在/etc/redis/下让每个实例对应用户自己的 conf 文件。以下是我在实际环境中长期使用的一个最小可运行配置逐段解释# 监听地址。默认127.0.0.1只允许本机访问跨机部署时改成内网IP bind 0.0.0.0 # 保护模式。bind非本机地址时建议显式设置protected-mode no或配合密码 protected-mode yes # 端口 port 6379 # 以守护进程方式后台运行 daemonize yes # pid文件便于systemd和服务脚本管理 pidfile /var/run/redis_6379.pid # 日志级别和路径 loglevel notice logfile /var/log/redis_6379.log # 数据持久化目录 dir /data/redis/6379 # RDB快照规则默认规则即可 save 900 1 save 300 10 save 60 10000 # 开启AOF并设置每秒同步 appendonly yes appendfsync everysec有几个参数需要展开讲。bind配置是我见过翻车最多的点。默认的bind 127.0.0.1在单机测试时没问题但一旦打算用其他机器连接很多人会改成bind 0.0.0.0。在 Redis 3.2 之后只要不是绑定本地回环地址protected-mode默认开启会拦截非本机请求。所以要么显式设置protected-mode no要么加上requirepass密码——后一种更安全我强烈建议在生产环境设置密码。save参数的含义是“多少秒内有多少次修改就触发 RDB 快照”比如save 900 1表示 900 秒内至少 1 次写入就执行 BGSAVE。如果只是做缓存不要求持久化可以全部注释掉但既然搭了完整环境我建议保留 RDB AOF 都开启两者并存不是多余的RDB 适合快速恢复全量数据AOF 则能尽量缩小丢失窗口。dir参数决定 RDB 文件和 AOF 文件存放位置。这地方要提前建好目录且确保运行 Redis 的用户有写权限否则启动时不会报错但第一次持久化时会失败日志里看到Cant open the append-only file才意识到。2.2 使用 systemd 管理 Redis 进程CentOS 7 已经全面使用 systemd。网上很多教程还在写/etc/init.d/redis那种 SysV 风格的脚本不建议照搬。写一个干净的 unit 文件才便于管理启动、停止、开机自启。cat /etc/systemd/system/redis-6379.service EOF [Unit] DescriptionRedis 6379 Afternetwork.target [Service] Typeforking ExecStart/usr/local/redis/bin/redis-server /etc/redis/redis_6379.conf ExecStop/usr/local/redis/bin/redis-cli -p 6379 shutdown PIDFile/var/run/redis_6379.pid Restartalways [Install] WantedBymulti-user.target EOF systemctl daemon-reload systemctl enable redis-6379 systemctl start redis-6379这里用的是Typeforking因为配置文件里已经设置了daemonize yesRedis 主进程会 fork 出子进程后退出systemd 通过 PIDFile 跟踪实际服务进程。如果没设置daemonize则应该用Typesimple并让ExecStart前台运行两种方式不通用。启动后检查# 查看端口监听 ss -tlnp | grep 6379 # 用redis-cli做基础验证 /usr/local/redis/bin/redis-cli -p 6379 ping # 返回 PONG # 用benchmark做个冒烟测试 /usr/local/redis/bin/redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000redis-benchmark在单机部署后值得跑一下它的输出包括 QPS、平均延迟、P99 延迟等。我通常在业务接入前先做个基线后续调内核参数或改配置时再进行对比能快速发现变化。2.3 单机验证与常用运维命令单机部署完成后有几个命令是后面搞哨兵和集群都必须依赖的建议现在就练熟# 查看当前节点信息 redis-cli -p 6379 info server redis-cli -p 6379 info replication redis-cli -p 6379 info memory # 查看慢查询 redis-cli -p 6379 slowlog get redis-cli -p 6379 config get slowlog-log-slower-than redis-cli -p 6379 config set slowlog-log-slower-than 10000 # 安全关闭 redis-cli -p 6379 shutdown nosaveshutdown nosave表示不执行 RDB 保存直接退出适合临时维护正常关闭应使用redis-cli shutdown。集群模式下不能用普通的 shutdown而要用redis-cli -c --cluster shutdown这个后面会提到。另一个容易被忽略的是config set在线修改。注意它只对运行期生效重启会丢失需要用config rewrite把改动写回配置文件。这个机制在排查问题的时候非常有用比如需要临时调大maxmemory而不想重启实例。3. 哨兵模式从主从复制到自动故障转移3.1 先搭主从replicaof 的配置逻辑哨兵模式不是悬空的它的底层必须先有一套主从复制。我在一台服务器上开三个实例来演示6379 作为 master6380 和 6381 作为两个 slave。生产环境当然建议分布在不同的物理机器上但同一个机器的多实例可以完整验证配置和故障转移逻辑。主库的配置就是前面单机配置无需额外改动。从库配置里需要加一行# 6380实例 port 6380 # 指向主库 replicaof 127.0.0.1 6379在 Redis 5.0 之前这个指令叫slaveof5.0 之后官方改为replicaof但slaveof还兼容。我见过有人用旧版命令在新版本配置里被拒绝所以统一用replicaof更稳妥。配置好之后启动 6380 实例用redis-cli -p 6380 info replication查看role:slave master_host:127.0.0.1 master_port:6379 master_link_status:up offset:...master_link_status为up说明复制链路正常。这里有一个常见坑如果 master 上设置了requirepass从库必须同步配置masterauth否则复制会被密码验证挡住日志里不断报MASTER abort。# 从库也要配置 masterauth your_redis_password不过本文主从演示暂时不开启密码因为后续故障转移逻辑更清晰少一个变量干扰。实际生产环境一定要开密码后面安全章节我会说明哨兵模式下多节点密码的联动配置。3.2 三个哨兵节点配置项均与故障判定相关哨兵本身也是一个 Redis 进程只是运行在特殊的 sentinel 模式。最少需要启动一个哨兵但生产环境至少 3 个哨兵节点才能满足多数派裁决。这里我在同一台机器上分别用 26379、26380、26381 三个端口模拟三个哨兵节点。以下是哨兵的核心配置port 26379 daemonize yes pidfile /var/run/redis-sentinel-26379.pid logfile /var/log/redis-sentinel-26379.log # 监控哪个master取什么名字几个哨兵可以认定故障 sentinel monitor mymaster 127.0.0.1 6379 2 # 多少毫秒内没响应认为主观下线 sentinel down-after-milliseconds mymaster 5000 # 故障转移后的从库同步超时 sentinel failover-timeout mymaster 15000 # 故障转移时最多几个从库同时同步新主 sentinel parallel-syncs mymaster 1逐项解释。sentinel monitor mymaster 127.0.0.1 6379 2的最后一个数字是 quorum表示几个哨兵认为 master 主观下线后才判定为客观下线并触发故障转移。3 个哨兵配2意味着至少 2 个哨兵不约而同地认为 master 挂了。为什么不用 3因为一旦 quorum3正常情况下去掉一个哨兵节点就永远无法达成多数故障转移会被卡住。quorum 的另一种理解是“最少需要几个哨兵投票才能开始切换”。down-after-milliseconds是哨兵和 master 之间 PING 的超时阈值。注意如果网络本身有抖动设太短会频繁误判触发无意义的切换我这里的 5000 是为了快速演示故障转移生产环境通常设 30000 以上还要结合业务对中断时间的容忍度来确定。parallel-syncs是故障转移后同时允许几个从库同步新主库。设 1 意味着一个同步完成后再同步下一个避免大实例全量同步时把所有从库带宽打满。三个哨兵节点除了端口不同配置内容几乎一样。启动方式/usr/local/redis/bin/redis-sentinel /etc/redis/sentinel_26379.conf启动后哨兵会重写自己的配置文件会把发现的从库自动记录进去。看到日志里出现fixed-slave或slave就说明哨兵已经发现了所有从节点。3.3 手动干掉 master故障转移的完整观测准备好了所有环境现在做故障注入。在另一终端不断向 master 写入数据然后直接 kill master 进程redis-cli -p 6379 debug sleep 30 kill -9 $(cat /var/run/redis_6379.pid)或者更真实一点kill -9 PID然后观察哨兵日志。日志推进顺序大致是sdown sentinel mymaster 127.0.0.1 6379 odown sentinel mymaster 127.0.0.1 6379 #quorum 2/2 new-epoch 1 try-failover master mymaster 127.0.0.1 6379 vote-for-leader ... elected-leader master mymaster ... failover-state-select-slave master mymaster ... selected-slave slave 127.0.0.1:6380 ... failover-state-send-slaveof-noone ... failover-end master mymaster ... switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380注意switch-master之后 master 已切换为 6380。这时执行redis-cli -p 6380 info replication # 可以看到 role:master redis-cli -p 6381 info replication # 可以看到 master_host:127.0.0.1 master_port:6380如果只启动了 1 个哨兵你是看不到上述完整过程的因为 quorum 永远不够。这也是我坚持至少 3 节点做验证的原因。3.4 哨兵高可用实操中的三个隐蔽问题第一哨兵本身也是单点。如果哨兵进程和 master 在同一台机器机器一挂既丢 master 又丢哨兵的投票权。生产环境要么把哨兵放在独立机器要么至少保证哨兵节点不全部和数据库节点重叠。第二密码环境的配置联动。Redis 5.0 之后master 设置requirepass后哨兵配置文件里必须设置sentinel auth-user mymaster redis_sentinel_user sentinel auth-pass mymaster the_password同时所有从库的配置里必须设置masterauth否则主从复制和故障转移后的重新复制都会失败。这个是我踩过最深的一个坑故障转移后新主库已经选出来了但从库连不上新主库一直SYNC超时。排查到最后发现就是masterauth没有同步更新。注意故障转移后从库可能自动重写配置但主库的密码发生了变化从库并不会自动感知还是要预先配置。第三客户端也需要感知 master 变化。很多客户端库支持通过哨兵地址获取当前 master比如 Java 客户端的RedisSentinelPool。如果在客户端里硬编码了 master 的 IP:Port那么哨兵切换后你的应用就断了。这是“哨兵模式生效了但业务还是连不上”的最常见原因配置之外的架构问题。4. 集群模式三主三从手把手搭建与验证4.1 集群规划端口、目录与配置文件的划分在哨兵模式的实践中数据还是写在一台机器上。集群模式则把数据按槽位分散到多个主节点每个主节点至少配一个从节点作为冗余。我采用经典的三主三从布局6 个 Redis 实例端口为 7000-7005。前三个是主节点后三个是对应的从节点。这种单机多实例的做法适用于测试和验证但必须明白在单台物理机上的集群并不能替代真实的多机容灾一旦宿主宕机所有主从都不可用。真实生产环境至少要分布在 3 台服务器上每台各开两个实例。我的目录结构如下/data/redis-cluster/ 7000/redis.conf 7000/data/redis.log 7001/redis.conf 7001/data/redis.log # ... 以此类推每个实例的配置大体相同差异主要是端口。以 7000 为例port 7000 daemonize yes dir /data/redis-cluster/7000 pidfile /data/redis-cluster/7000/redis_7000.pid logfile /data/redis-cluster/7000/redis.log appendonly yes appendfsync everysec # 集群开关 cluster-enabled yes # 集群状态文件 cluster-config-file nodes-7000.conf # 节点超时阈值 cluster-node-timeout 15000集群模式的关键参数是cluster-enabled yes和cluster-config-file nodes-7000.conf。后者是集群内部状态文件由 Redis 自动维护记录当前节点的 node-id、已知的集群节点列表、槽位分配等信息。手动编辑它没有任何意义启动时如果损坏Redis 无法加入集群把该文件删掉后重启如果该节点已经有数据且有槽位删除会导致集群数据不可访问节点初始化阶段无此问题。cluster-node-timeout决定节点间互相判定超时。如果设太短网络稍有抖动就会引发频繁的主从切换设太长会导致故障恢复较慢。15000 是我试过比较稳的值。4.2 创建集群槽位分配、主从归属自动完成所有 6 个实例启动后用 Redis 自带的集群管理工具创建集群/usr/local/redis/bin/redis-cli --cluster create \ 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \ 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \ --cluster-replicas 1--cluster-replicas 1的意思是每个主节点配 1 个从节点。工具会先尝试把 16384 个哈希槽平均分配给 7000、7001、7002 三个主节点然后把 7003、7004、7005 分配为它们的从节点。执行后会让你确认分配方案输入 yes 完成。验证集群状态redis-cli -p 7000 cluster info # cluster_state:ok # cluster_slots_assigned:16384 # cluster_slots_ok:16384 redis-cli -p 7000 cluster nodescluster nodes输出中能看到每个节点的 node-id、IP:Port、角色master/slave、从属关系、负责的槽位段。注意槽位是连续段比如 0-5460、5461-10922、10923-16383 这样分配。这时候需要特别提醒一个细节如果某个实例之前没有清空数据创建集群时可能报 “Node ... is not empty”。需要确认里面确实没重要数据后用redis-cli -p 7000 flushall清空再删除集群状态文件重启后再加入。4.3 验证集群路由逻辑与故障转移集群模式下key 会被哈希到某个槽上。客户端必须知道 slot - node 的映射关系才能把请求发到正确的节点。redis-cli 有集群模式加-c参数即可redis-cli -c -p 7000 127.0.0.1:7000 set name zhangsan # - Redirected to slot [5798] located at 127.0.0.1:7001 OK 127.0.0.1:7000 get name # - Redirected to slot [5798] located at 127.0.0.1:7001这就是集群路由的直观体现。单个 key 存在哪个节点取决于CRC16(key) % 16384计算出来的槽位号。如果直接用普通模式redis-cli -p 7000执行set name hank且该 key 不在当前节点会遇到MOVED报错而不是自动跳转很多刚接触集群的人会被这个报错吓到以为是配置错误其实就是客户端没开启集群模式或没有实现集群协议。多个 key 的操作在集群里有限制因为不同 key 可能落在不同节点无法保证原子性。解决办法是用 hash tagset {user123}.name zhangsan set {user123}.age 30花括号里的部分会被单独拿出来做 CRC16 运算这样同一个用户的多个 key 会落到同一个槽位这两个键就支持事务或多键操作。这是我做业务改造时最常用的一招。接下来做故障转移验证。杀掉 7000 这个主节点kill -9 $(cat /data/redis-cluster/7000/redis_7000.pid)等待 15 秒左右对应cluster-node-timeout配置再查看集群状态redis-cli -p 7001 cluster nodes可能会看到如下状态xxxx... 127.0.0.1:7003 slave 7000-nodeid 0 connected xxxx... 127.0.0.1:7000 master,fail - ...注意如果 7003 是从属于 7000 的从节点它会被自动提升为新的 master接管原 7000 的槽位。日志中会出现# Failover election won by ... # New master is ...实际上集群的故障切换流程是从节点向所有主节点发起竞选投票获得多数票后提升自己。这也是为什么集群至少需要 3 个主节点的理论依据——如果只有 2 个主节点某一主节点故障时剩余节点数不足以形成多数派。被 kill 掉的 7000 重新启动时会发现自己的 master 身份已经被 7003 取代。它会自动以从节点的身份加入集群并从 7003 同步数据。这种自动降级/升级不需要人工干预。4.4 集群模式下几个容易忽略的坑第一个集群总线端口。Redis 集群里每个节点除了监听配置的端口外还会监听一个“集群总线端口”通常是配置端口10000。7000 的集群总线就是 17000。如果服务器有防火墙只放行了 7000-7005 而没放行 17000-17005会发现节点之间一直处于握手失败状态cluster nodes里看到的节点状态可能是handshake或disconnected。第二个redis-cli --cluster命令只是初始化工具不是常驻进程。节点之间的心跳、故障检测、槽位迁移都靠集群节点自身完成。有的团队以为必须保持某个专门进程不退出这是误解。第三个槽位迁移期间请求会返回ASK或MOVED。MOVED表示槽位已经永久迁移到另一个节点ASK表示槽位正在迁移中需要先发送ASKING。大部分客户端库会自动处理但如果用脚本直接对接必须自己实现这两个分支否则迁移期间大量写失败。第四个集群模式下keys *这种命令是无效的因为它需要把所有节点遍历一遍。我的做法是用redis-cli --cluster keys或写个小工具扫描各节点然后在业务层改成按用户维度取 key尽量避免遍历需求。5. 三种模式通用的系统调优与踩坑记录5.1 内核参数内存分配、THP 与连接队列Redis 正常运行还需要做几个操作系统层面的调整这一步在单机、哨兵、集群模式下都是通用的。不调整也能跑但高并发或高负载下会遇到各种奇怪的延迟和阻塞。/etc/sysctl.conf中加入# 允许 Redis 在内存不足时也能执行后台保存等操作 vm.overcommit_memory 1 # 关闭 THP 的自动内存整理避免 fork 后发生内存页移动 # 实际生效需要额外关闭 THP见下文 vm.max_map_count 262144 # 增大 TCP backlog net.core.somaxconn 511vm.overcommit_memory是一个经常被忽略的参数。Redis 执行 BGSAVE 或 AOF 重写时需要 fork 子进程fork 要复制父进程的内存页表。如果系统内存余量不足且 overcommit 设置为 0即内核按保守策略拒绝明显超过物理内存的请求fork 可能会直接失败后台持久化也就失败了。而 Redis 在启动时会打印警告提醒用户这算是最常见的启动警告之一。THPTransparent Huge Pages对 Redis 的负面影响比较隐蔽。内核默认会尝试用 2MB 大页来减少 TLB miss但这会导致 fork 之后如果原进程写入了某个页整个 2MB 页都需要复制而不仅仅是 4KB 页极端情况下造成写时复制放大和延迟抖动。关闭方式echo never /sys/kernel/mm/transparent_hugepage/enabled要持久化可以在/etc/rc.d/rc.local中追加该命令并给 rc.local 授权。net.core.somaxconn关系到 TCP 层的 accept 队列长度。Redis 的tcp-backlog默认值是 511如果系统 somaxconn 小于它高并发连接建立时会被内核丢弃客户端表现为连接超时。5.2 内存淘汰策略与避免 swap单机、哨兵、集群模式都面临同样一个问题内存满了怎么处理。默认情况下maxmemory参数不设置意味着 Redis 可以一直用到系统内存耗尽然后操作系统开始 swapRedis 的性能会急剧恶化——这是一切诡异的可能来源。我建议根据业务容量预留 20%-30% 余量后设置maxmemory同时指定淘汰策略maxmemory 2gb maxmemory-policy allkeys-lrumaxmemory-policy常用选项有策略行为适用场景noeviction不淘汰写入报错不允许丢数据的场景allkeys-lru所有 key 按 LRU 淘汰偏缓存场景volatile-lru仅带 TTL 的 key 按 LRU 淘汰部分数据可过期部分不能allkeys-random随机淘汰均等访问、无热点场景volatile-ttl淘汰 TTL 剩余时间最短的 key需要优先淘汰快过期数据的场景注意某些老教程里把volatile-lru当推荐配置理由是不动没有设置 TTL 的永久 key。但实际场景中如果大部分 key 都没有 TTL这个策略等于没有淘汰能力内存照样爆。如果想限制 Redis 只能使用物理内存而不是 swap可以在配置里增加maxmemory的同时在 systemd 单元里加MemoryMax限制或者干脆给 Redis 进程设置oom_score_adj避免它在内存紧张时成为首选牺牲品。用redis-cli info memory可以查看碎片率、峰值内存等数据。mem_fragmentation_ratio明显大于 1.5 时说明内存碎片比较严重重启实例或使用memory purge能部分缓解但最根本的方式是提前用 jemalloc 并避免频繁写入大量小 key。5.3 基础安全加固密码、命令重命名与网络暴露单机或者集群模式下如果 Redis 端口暴露在公网被扫描后写入公钥、植入挖矿脚本这类事故并不是新闻。下面是我在部署时统一执行的加固项不分模式照做就是。第一所有节点都设置访问密码requirepass your_complex_password如果是集群模式密码必须一致。更准确的说法是每个节点的认证密码要统一同时所有节点配置masterauth否则集群内的主从同步会中断。集群模式开启密码的维护成本不小因为节点之间通信也需要认证但这是安全的底线。第二禁止危险命令。比如FLUSHALL、FLUSHDB、KEYS、CONFIG、EVAL等。有些命令在排查问题时很有用所以我的做法是只禁用部分rename-command CONFIG rename-command FLUSHALL rename-command FLUSHDB rename-command设为空字符串表示完全禁用。注意Redis 7.0 引入了更细粒度的 ACL 机制可以用redis-cli -p 7000 acl setuser为不同业务账户分配最小权限比单口令全部权限优雅得多。如果用的是 7.x建议直接用 ACL。第三bind只监听内网。不要图省事绑0.0.0.0然后靠防火墙挡。正确做法是bind 192.168.1.100 192.168.1.101如果同机多实例可以绑定本机内网 IP 加回环地址。另外集群模式下各节点之间通信端口必须能互相访问安全组和防火墙都要放行业务端口和集群总线端口端口10000。5.4 我在排查中最常遇到的四个问题数据库之间复制断开的排查。如果info replication中看到master_link_status:down第一件事不是重启而是看 master 的日志和从库的日志。常见原因包括网络闪断、masterauth密码不匹配、主库执行了BGSAVE导致耗时过长、从库内存不足等。网络闪断后 Redis 会自动重连除非配置了repl-timeout较小。AOF 文件越来越大影响故障恢复速度。开启 AOF 后如果不做定期重写文件占用会持续增长。Redis 会自动触发重写但触发条件依赖auto-aof-rewrite-percentage和auto-aof-rewrite-min-size。我见过最严重的一次是 AOF 文件增长到 30GB故障重启花了将近半小时。建议设置下限到一个可接受的值auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 256mb慢查询不一定只来自大 key。有些慢查询是网络抖动导致的客户端超时重试有些是KEYS *这种 O(N) 命令。我习惯把slowlog-log-slower-than设为 10msslowlog-max-len设大一些然后定期导出分析。如果大部分慢命令是BGSave或AOF rewrite带来的 fork 阻塞那就是系统参数没调好而不是命令本身的问题。集群模式扩容时槽位迁移速度不可控。使用redis-cli --cluster reshard做槽位迁移时默认迁移速度不够快可以通过--cluster-migrate-timeout等参数调整。如果某些槽位上的 key 数量极大迁移会持续很长时间。稳妥的方式是分批迁移并监控网络 IO 和源节点、目标节点的负载。写在最后我从最早只用单机 Redis 做缓存到后来被迫应对主节点宕机再到业务量增长必须用集群这条路每一步的教训都不少。CentOS 7 上的 Redis 部署并不难难的是理解背后几个核心机制单机模式让你理解配置项和持久化哨兵模式让你理解多数派决策和故障切换的微妙集群模式让你理解数据分片和分布式的现实复杂度。如果你跟我一样是在存量业务上推进这些改造建议不要想着一天把所有模式都换掉。先把单机实例的持久化、备份、监控做好再逐步引入主从和哨兵当哨兵切换验证足够顺手后再讨论是否上集群。毕竟高可用是一个系统工程不是装好某个软件就自动实现的。最后再分享一个实操技巧做任何切换演练前记得先确认客户端连接池已经设置了合理的超时时间和重试机制否则就算哨兵切换成功业务侧也会因为连接池里缓存了旧连接而继续报错。这个细节我在第一次切换测试时没注意结果被线上故障打了个措手不及。