干这行这些年,Redis的部署是每个运维和开发都绕不过去的基本功。但"部署Redis"这个短语背后,其实藏着完全不同的东西:单机版、主从复制、哨兵模式、Cluster集群,它们解决的问题不同,部署细节和踩坑方式也完全不一样。很多人上来就装个redis-server,数据一多就抓瞎;也有人一步到位上集群,结果发现小业务根本用不着那么复杂,白白增加维护成本。这篇文章我想把四种主流架构的部署完整梳理一遍,从最基础的单机到分片集群,每个架构讲清楚它能解决什么问题、怎么装、关键参数怎么配,以及我在线上环境里实际碰到过的坑。无论是第一次搭Redis的新手,还是想系统梳理架构差异的开发者,都能从这里找到可以参考的落脚点。
1. 部署前的选择:Redis四种架构到底在解决什么
1.1 别把"能跑"当成"部署完成"
先聊一个我在不少团队里见过的情况:开发环境里Redis崩了没人管,测试环境一重启数据全没了,到了快上线才发现生产环境根本没有高可用方案。这些问题的根源几乎都是同一个——部署之前没有认真想过"我到底需要哪种架构"。
Redis官方给了一套从简单到复杂的演进路线:单机版、主从复制、哨兵模式、Cluster集群。这四者不是互相替代的关系,而是在不同量级和不同可用性要求下的选择。单机版解决"有没有Redis用"的问题;主从复制解决"数据多一份备份、读压力能分担"的问题;哨兵模式解决"主节点挂了能不能自动切换"的问题;Cluster集群解决"单机内存装不下、写性能上不去"的问题。
很多人在这一步就选错了。我见过有团队数据量只有几百MB,却搭了三主三从六个节点的集群,日常维护成本远超收益;也见过某业务单实例内存已经到60GB,写流量把CPU打满,还在硬扛而不是分片。架构不是越高级越好,而是越匹配越好。
1.2 四种架构的核心差异对比
在动手部署之前,先用一张表把四种架构的关键差异摆出来,后面每一步部署都能对应回去:
| 架构 | 单机版 | 主从复制 | 哨兵模式 | Cluster集群 |
|---|---|---|---|---|
| 数据分片 | 不支持 | 不支持 | 不支持 | 支持(16384个槽位) |
| 高可用 | 无 | 无自动切换 | 自动故障转移 | 主从自动切换 |
| 横向扩展 | 不可扩展 | 只扩读,不扩写 | 只扩读,不扩写 | 读写均可扩展 |
| 部署复杂度 | 低 | 低 | 中 | 高 |
| 适用数据量 | 单机内存内 | 单机内存内 | 单机内存内 | 内存可横向叠加 |
| 日常运维 | 简单 | 较简单 | 中等 | 较复杂 |
这张表我从多个维度做了对比。注意主从和哨兵模式的容量上限其实是一样的,因为它们底层都是"一个主节点存全量数据",从节点只是副本。很多人误以为加了从节点就能存更多数据,这是理解上的偏差。真正能解决容量问题的只有Cluster。
1.3 版本选型:7.x是当前更值得的选择
部署前还要定版本。Redis 5.x、6.x、7.x现在都在线上环境里存在,但新部署我建议直接上7.x版本。理由是:Redis 7.0之后AOF文件和RDB文件可以混用,重启恢复速度显著提升;I/O线程、缓存清理方面也有不少改进。更重要的是,7.x的命令和配置项和6.x基本兼容,网上主流资料都能参考,找人排障也容易。
本文后续所有部署操作以Redis 7.0.x为例,如果你用的是6.x,配置项名称基本一致,个别参数(比如replicaof在5.x里叫slaveof)需要注意下差异。
2. 单机版安装:把Redis当作一个正经服务来托管
2.1 源码编译安装的完整过程
单机版虽然简单,但安装方式会直接影响后续好几年使用体验。我不太推荐用apt-get或yum直接装系统自带的Redis包,因为那些版本往往偏老,而且目录结构分散,后面要排查问题很麻烦。我更习惯源码编译安装,可控性强,目录干净。
以一台干净的Linux服务器为例,安装过程大概是这样的:
# 安装编译依赖 apt-get update && apt-get install -y build-essential tcl # 下载并解压 wget https://download.redis.io/releases/redis-7.0.12.tar.gz tar xzf redis-7.0.12.tar.gz cd redis-7.0.12 # 编译并安装到指定目录 make -j$(nproc) make install PREFIX=/usr/local/redismake install PREFIX=/usr/local/redis这步是关键,它会把redis-server、redis-cli这些可执行文件统一放到/usr/local/redis/bin下面。之后我习惯再做一步软链接,方便命令直接可用:
ln -s /usr/local/redis/bin/* /usr/local/bin/这样以后执行redis-cli、redis-server都不用写全路径。编译过程中如果报缺少tcl的错误,就是2.0那步没装好;如果make test你不想跑,跳过也可以,不影响实际使用。
2.2 systemd托管:让Redis开机自启、崩溃自动拉起
编译完只是第一步。一个正经的Redis服务必须能被系统守护进程管理,这样开机自启、异常退出自动拉起、日志统一收集这些能力才会有。
在/etc/systemd/system/redis.service里新建一个unit文件:
[Unit] Description=Redis Server After=network.target [Service] Type=forking ExecStart=/usr/local/redis/bin/redis-server /etc/redis/redis.conf ExecStop=/usr/local/redis/bin/redis-cli -p 6379 shutdown Restart=always User=redis Group=redis PIDFile=/var/run/redis_6379.pid [Install] WantedBy=multi-user.target这里有几个细节值得注意。
Type=forking意味着Redis进程启动时会fork一个子进程,主进程退出,这时systemd通过PIDFile来跟踪实际服务进程。要配合这个方式,redis.conf里必须设置daemonize yes,否则Redis会在前台运行,systemd会认为启动失败。
我建议单独创建一个redis用户,不要用root跑Redis,安全风险和文件权限问题都会少很多:
useradd -r -s /bin/false redis mkdir -p /etc/redis /var/lib/redis /var/log/redis chown redis:redis /var/lib/redis /var/log/redis对应的redis.conf关键配置如下:
bind 0.0.0.0 protected-mode yes port 6379 daemonize yes pidfile /var/run/redis_6379.pid logfile /var/log/redis/redis-server.log dir /var/lib/redis appendonly yes appendfsync everysec requirepass your-strong-password启动后就检查一下状态:
systemctl daemon-reload systemctl start redis systemctl enable redis redis-cli -a 'your-strong-password' ping能返回PONG就说明单机版部署完成。从这一步开始,Redis就是一个会被系统自动守护的正经服务了。
2.3 单机版最容易埋下的三个隐患
单机版部署看似简单,但我在实际环境里见过太多因为细节没处理到位导致的事故,挑三个高频问题重点说一下。
第一个是持久化配置缺失。默认配置下Redis只是内存缓存,重启数据就全部丢失。如果你把Redis当缓存用,这没问题;但如果里面放了用户会话、任务状态这类不能丢的数据,就必须开启appendonly yes。我习惯把AOF和RDB同时打开,兼顾恢复速度和数据安全性,appendfsync everysec是性能和数据安全的平衡点。
第二个是密码和网络暴露。很多内网部署图省事,密码不设,bind 127.0.0.1也不改。一旦Redis对公网开放,扫描工具分分钟就能把你的实例打成肉鸡,比特币勒索挖矿的案例每年都有不少。我一般至少做到两点:requirepass设置强密码、protected-mode yes保持开启。
第三个是内存上限不设防。不设置maxmemory的话,Redis会一直吃机器内存直到OOM被内核杀掉,这在生产里是灾难。我通常要么在配置里写maxmemory 4gb,要么加一句maxmemory-policy allkeys-lru做缓存淘汰,具体策略看业务用途。
到这里,单机版的部署和守护就算完整了,下面开始往多节点方向扩展。
3. 主从复制:从一台到多台,先搞懂同步机制
3.1 主从架构的部署配置
主从复制解决的核心问题是两个:读压力分担和数据冗余。所有写操作走主节点,读操作可以分散到从节点;主节点数据实时同步到从节点,相当于自动多了一份热备。但注意,从节点默认是只读的,写操作不允许。
我从主从复制的原理讲起,因为它决定了后面哨兵和集群的理解难度。
主从同步分为全量同步和增量同步两个阶段。全量同步发生在从节点第一次连接主节点或者同步链路断开太久时,主节点会触发一次BGSAVE生成RDB快照发给从节点,同时把生成快照期间新产生的写命令缓存到repl_backlog缓冲区,RDB传输完成后,再把这些积压的命令补发给从节点。增量同步则是同步链路建立后,主节点把新写入命令实时地推给从节点。
部署配置上,假设有master和slave两台机器,slave的redis.conf里只需要加一行:
replicaof 192.168.1.100 6379如果主节点开启了密码,还要加一行:
masterauth your-master-password启动从节点后,在任意一台机器上执行redis-cli -a password info replication,能看到主从链路的连接状态。重点关注master_link_status:up,只有这个值是up,才算真正同步成功。master_repl_offset和slave_repl_offset分别代表主从各自的同步偏移量,两个值越接近说明同步越实时。
如果你手头有多个Redis实例,但不方便直接改配置重启,还有一个不需要重启的临时方式。在从节点上执行REPLICAOF host port,也能实时把节点变成从节点。这个命令在测试和临时切换场景下非常好用。
3.2 同步链路维护的几个关键参数
主从架构搭起来容易,但让它长期稳定运行,需要调整几个默认参数。
repl-backlog-size这个参数特别容易被忽略。前面讲过,repl_backlog是主节点用来缓存最近写命令的环形缓冲区,默认只有1MB。如果从节点断连时间比较长,积压的主从差异超过了这个缓冲区,重连后就只能全量同步一次,对网络和主节点CPU压力都很大。我一般把它调到64MB甚至128MB,具体取决于你业务的写入量和从节点可能断连的时长。粗略估算:假设高峰期写入量10MB/s,想让从节点断连5分钟内能走增量同步,backlog至少要有10560=3000MB,这个数字看起来大,但实际上写命令不是实际数据大小,通常1MB/s的QPS已经很夸张了,64MB足够覆盖大多数场景。
min-replicas-to-write和min-replicas-max-lag这两个参数同样值得重视。它们解决的问题是:如果只有一个主节点孤军奋战,数据还没来得及同步给从节点,主节点就宕机了,那么这些数据就丢了。配置这两个参数以后,如果健康的从节点数量不达标,主节点会拒绝写入,用短暂不可用来换取数据不丢失。
min-replicas-to-write 1 min-replicas-max-lag 10这段配置表示:至少需要有1个从节点,且它的数据延迟不超过10秒,主节点才接受写入。在追求数据安全而不是极端可用性的场景里,这个设置很实用。
3.3 主从模式下最容易踩的坑
主从架构有一个天然痛点:主节点挂了,从节点不会自动变成主节点。等主节点恢复或者人工手动执行REPLICAOF NO ONE把某个从节点提升为主节点,这个过程往往需要几十秒甚至数分钟,业务写入在这段时间内是中断的。这也是为什么生产环境很少只用主从——它只是高可用的基础,而不是高可用本身。
另一个高频坑是从节点开启了持久化,但没有配置好数据目录。主从同步本身会自动把主节点的数据同步落地,如果从节点的dir目录权限有问题、磁盘写满,从节点会频繁尝试全量重同步,拖垮主节点磁盘I/O。我遇到过一整个下午主从之间反复全量同步,原因就是从节点磁盘写满,每次同步都半途而废。
还有过期键同步的问题。主节点删除过期key时会生成DEL命令同步给从节点,但如果从节点上key的过期时间和主节点不一致(比如配置了不同的lazyfree策略),可能出现从节点上已经逻辑过期但还没物理删除的key,在从节点上仍能读到。这类问题比较隐蔽,排查时如果只查业务逻辑通常找不到原因,要看主从的过期策略配置是否一致。
4. 哨兵模式:让故障转移自动发生
4.1 哨兵架构和主从的本质区别
说完主从的痛点,哨兵模式解决的问题就很明确了:自动监控主节点状态,主节点挂了自动选出一个从节点提升为主节点,同时把新的主节点地址通知给客户端。哨兵本身不存业务数据,它是一个独立的Redis进程,职责是"看门"。
哨兵架构的部署套路是:一主两从,再配三个哨兵实例。为什么是三个哨兵而不是一个?这涉及到哨兵判断节点是否故障的协作机制。哨兵判断某个主节点"主观下线"是本地视角,只有超过quorum数量的哨兵都认为主节点挂了,才判定为"客观下线",触发故障转移流程。quorum值通常配置为2,在三个哨兵的情况下,即使其中一个哨兵本身出问题,另外两个还能达成共识,不会出现单点误判。
这个"多数派"机制是整个Redis高可用的核心设计,在后面Cluster里也是一样的逻辑。把它理解透了,故障转移的行为就变得完全可以预测了。
4.2 三个哨兵与仲裁机制的部署实践
哨兵模式里的角色分配通常是:一个主节点、两个从节点、三个哨兵进程。哨兵进程可以部署在应用服务器上,也可以和Redis节点混部,只要网络能互通即可。
哨兵的配置文件独立于Redis主配置,通常叫sentinel.conf:
port 26379 dir /tmp daemonize yes logfile /var/log/redis/sentinel.log sentinel monitor mymaster 192.168.1.100 6379 2 sentinel auth-pass mymaster your-password sentinel down-after-milliseconds mymaster 5000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 15000逐项解释:
sentinel monitor mymaster 192.168.1.100 6379 2:监控名字叫mymaster的主节点,后跟主节点IP和端口,最后的2是quorum值,即至少2个哨兵同意才执行故障转移。down-after-milliseconds 5000:哨兵连续5秒联系不上主节点,开始判定主观下线。parallel-syncs 1:故障转移时,同时允许几个从节点向新主节点同步数据,设为1是避免新主节点瞬间承受多个全量同步的请求。failover-timeout 15000:故障转移流程的总超时时间。
另外,如果你是容器部署,一定不要用localhost或127.0.0.1去指定主节点地址,哨兵会把这个地址同步给客户端,容器里127.0.0.1指向的是客户端自己,会导致连接失败。我在这里踩过坑,折腾了将近一下午。
哨兵配置文件的另一个特别之处是:它会自己重写。哨兵运行过程中会动态修改配置文件,把监控到的节点状态、切换记录持久化。所以不要手动去编辑正在运行的哨兵配置文件,改完也会被覆盖。
启动三个哨兵方式一样:
redis-server /etc/redis/sentinel.conf --sentinel或者用:
redis-sentinel /etc/redis/sentinel.conf启动后用redis-cli -p 26379 sentinel master mymaster可以查看主节点监控信息,用sentinel get-master-addr-by-name mymaster可以直接拿到当前主节点的IP和端口。这些都是客户端接入哨兵模式时最常用的命令。
4.3 故障转移的全过程复盘
我把哨兵模式下一次真实的故障转移过程拆开来看,这样你能直观明白为什么这套机制能保证高可用。
场景:主节点192.168.1.100突然宕机。Sentinel 1在5秒内发现联系不上主节点,给主节点标记为主观下线(S_DOWN)。此时Sentinel 2、Sentinel 3也陆续发现同样的问题,三者在互相通信后确认主节点客观下线(O_DOWN)。由于quorum设置为2,已经满足条件,开始选举。
选举过程中,哨兵之间会通过Raft算法的一致性协商选出一个Leader哨兵,由它负责执行故障转移。Leader哨兵从候选从节点中挑选一个作为新主节点,挑选规则是:优先级最高的(replica-priority数值越小越优先)、同步偏移量最大的、runId最小的依次比较。确定新主节点后,执行REPLICAOF NO ONE让它变为主节点,再通知其他从节点去同步新主节点。
整个过程在配置合理的情况下一般在15到30秒内完成。客户端需要做什么?它不能直接连接Redis端口,而是应该通过哨兵获取当前主节点地址,并且监听哨兵的+switch-master事件,一旦主节点发生变化,客户端自动切换连接。很多支持哨兵的客户端库已经封装好了这套逻辑,比如Java的Lettuce、Jedis的JedisSentinelPool、Python的redis.Sentinel等。
这里一定提醒一句:客户端必须支持哨兵协议,而不是简单地把Redis连接地址写成主节点IP。否则主节点一切换,客户端就彻底连不上Redis了。这个小细节在生产里引发的故障频率非常高。
5. Cluster集群:分片与横向扩展的部署实战
5.1 分片的底层逻辑:槽位、哈希和重定向
当单实例内存不足以装下业务数据,或者写QPS已经让单个Redis进程CPU吃不消时,就该上Cluster了。Cluster集群把整个数据空间划分为16384个槽位,每个主节点负责一部分槽位。写入某个key时,客户端对key计算CRC16哈希值,再对16384取模,得到槽位号,然后由负责该槽位的节点处理。
这带来一个新问题:key和节点之间的映射关系需要所有客户端都约定好。上面计算CRC16的方法是最简单的取模方式吗?不是,实际上还支持hash tag的优化。比如{user1000}.name和{user1000}.age,只要key里包含{}包裹的部分,Cluster只对花括号内的内容计算哈希,这样两个key会落到同一个节点上。这是实现批量操作和多key事务的关键手段,因为跨槽位的多key操作在Cluster里默认是禁止的。
额外说一句,Cluster集群还有一个硬约束:只有0号数据库可用,多数据库这种老用法在集群模式里彻底行不通。所以如果业务代码里有select 1之类的操作,上集群前就要重构。
5.2 三主三从集群的完整创建流程
集群最少需要6个节点(3主3从)才能既分片又保证高可用。我这里用3台机器,每台机器上跑两个Redis实例,端口分别是7000、7001、7002、8000、8001、8002。每台机器的配置基本一样,只有端口、目录不同。
节点的redis.conf文件要增加集群相关配置:
port 7000 daemonize yes dir /var/lib/redis-cluster/7000 cluster-enabled yes cluster-config-file nodes-7000.conf cluster-node-timeout 15000 appendonly yes appendfsync everysec requirepass your-password masterauth your-passwordcluster-enabled yes是开关;cluster-config-file是集群保存节点状态的文件,由Redis自动维护;cluster-node-timeout是节点间相互通信判定超时的时间。注意masterauth这里必须设置,因为主从节点之间进行同步时需要认证,很多人在这一步漏配,导致集群创建出来以后从节点一直同步失败。
注意,7000端口是用来接收客户端请求的,但节点之间还有一个集群总线端口,固定为端口+10000,也就是17000。你如果配了防火墙,只放行了7000端口而没放行17000,集群节点之间通信就会失败,表现形式非常诡异:主从切换无法完成、槽位检查飘红、客户端连接报错。这个坑我至少帮人排查过三四次。
六台节点的配置都准备好后,用官方自带的命令一键创建集群:
redis-cli --cluster create \ 192.168.1.10:7000 192.168.1.10:8000 \ 192.168.1.20:7001 192.168.1.20:8001 \ 192.168.1.30:7002 192.168.1.30:8002 \ --cluster-replicas 1 -a your-password--cluster-replicas 1表示每个主节点配1个从节点。命令执行后会先展示一份槽位分配计划,确认后输入yes,集群就创建完成了。检查状态用:
redis-cli -c -p 7000 -a your-password cluster info redis-cli -c -p 7000 -a your-password cluster nodescluster info里重点看cluster_state:ok,cluster nodes里能看到每个节点的角色、槽位范围、连接状态。这里有个细节:普通redis-cli访问集群节点必须加-c参数,否则明明在集群里,但访问不属于当前节点的key会直接报MOVED错误而不是自动跳转,很多新手会被这点搞懵。
5.3 集群的扩容与日常维护要点
集群的好处是支持在线横向扩展。新增一个主节点的流程大致是:
# 1. 把新节点加入集群 redis-cli --cluster add-node 192.168.1.40:7003 192.168.1.10:7000 -a your-password # 2. 从现有主节点迁移部分槽位给新节点 redis-cli --cluster reshard 192.168.1.40:7003 -a your-password执行reshard后按提示输入要迁移的槽位数和接收槽位的节点ID,然后选择从哪些节点迁出,Redis会自动把槽位对应的数据迁移过去。这个过程是在线进行的,客户端会感知到重定向(MOVED和ASK),但正常工作不受影响。为了数据安全,我都是先加从节点,再给新主节点做reshard,这样万一迁移过程中新主节点挂了,还有副本顶着。
日常运维上,有几个要点:节点数多以后,每个节点的nodes-*.conf文件不要手动修改,它由集群自动维护;进行大规格变更(比如批量重启节点)时要一个一个来,不要同时操作多个节点,防止触发集群选举风暴;监控上要重点盯cluster_state和跳槽告警,如果它从ok变成fail,通常意味着有主节点挂了并且没有从节点顶替,数据可能处于不可写状态。
6. 真实运维里的选型账:别只看架构图漂亮
6.1 不同业务场景下的部署建议
把四种架构都动手部署过一遍以后,我想把选型的经验做一次比较完整的总结。
如果是一个日活几千、数据量几百MB的内部系统,直接单机版就够用,外加每天做一次RDB备份。你不需要哨兵也不需要集群,多出来的复杂度只会增加你的工作量。
如果是用户量到了几十万、读多写少、业务不能接受长时间不可用的场景,主从加哨兵是最划算的组合。三个哨兵实例可以和应用服务器混部,不需要额外机器成本;一主两从既能抗住读流量,也能在主节点宕机时实现自动切换。
如果数据量到了几十GB甚至上百GB,单机内存已经难以承载,或者写流量已经让单节点CPU打满,就必须上Cluster了。Cluster的运维成本和故障排查复杂度会明显上一个台阶,但数据量和QPS摆在那里,没有更好的选择。
我这里再强调一下容易被忽略的点:主从加哨兵无法突破单节点的容量上限。很多团队把哨兵当成"高可用集群"来理解,这是不对的地方。哨兵只是把主从模式的故障切换自动化了,容量上限依然是单机的。如果业务数据已经超过单机合理内存(一般建议单节点不超过20-30GB),再多的哨兵也救不了你。
6.2 部署之外,运维成本同样需要被算进去
最后说点实在的。部署架构选型,除了看性能指标,还要把你后续的日常运维成本也算进去。单机版只需要维护一份配置,主从加哨兵要维护3个Redis实例加3个哨兵实例,Cluster要维护6个甚至更多节点。每次升级Redis版本、调整参数、排查慢查询,运维工作量都是线性上涨的。
我在实际项目里养成的习惯是,架构变更前先列一个"出现故障时谁能快速响应"的检查清单:如果主节点挂了,多久能恢复写?如果节点数据损坏,最坏丢多少数据?客户端能不能自动感知主从切换?监控告警里能不能第一时间看到连接异常?这些问题比部署速度更能反映一套架构是否适合你。
另外,部署时顺手把监控接好。redis_exporter配合Prometheus刮取Redis的指标很方便,几个黄金指标建议盯一下:connected_clients连接数、used_memory内存使用率、master_link_status主从同步状态、evicted_keys淘汰键数量、cluster_state集群状态。这些指标在故障发生时能帮你节约大量排查时间。
把架构部署好只是开始,真正让Redis稳定跑着的,是后续每一次配置调整时的谨慎和对异常指标的敏感。希望这篇梳理能给你一个相对完整的出发点,少踩几个我踩过的坑。