☰
Ubuntu 20.04上Redis Cluster缓存集群的搭建与优化实践
2026/10/9 6:38:22 网站建设 项目流程

做电商网站的这几年,Redis一直是我们在性能战场上的主力军。尤其是遇上大促、秒杀这种流量洪峰,数据库如果被直接打穿,那基本就是一场事故。我这次要聊的,是在Ubuntu 20.04上从零搭建并优化一套Redis缓存集群的过程,目标是让大规模电商网站的页面加载速度更快,同时把缓存效率榨干。这套方案我们已经在生产环境跑了小半年,效果稳定,所以把完整思路和操作细节整理出来,给正在被缓存架构困扰的朋友一个可以直接参考的路径。

不同于单机缓存或简单的主从模式,大规模电商场景下的Redis需要的是分片能力、横向扩展能力和故障自愈能力。光把集群跑起来不难,难的是在跑起来之后,怎么让命中率上去、延迟下来、内存不被浪费,以及应对热点key和大促流量。文章会按方案选型、部署落地、参数调优、场景设计、监控排障五个层面展开,每一步都有我实打实的实践经验。

1. 集群方案怎么选:为什么是Redis Cluster而不是主从或哨兵

先说结论:在“大规模、高并发、需要水平扩展”的电商场景里,Redis Cluster是唯一不需要外部组件就能实现自动分片和故障转移的官方方案。主从复制和哨兵模式虽然常见,但它们的本质都是一主多从——只有一个主节点负责写入,内存容量和写并发都有天花板。电商的商品库、库存、用户会话动辄几十GB甚至上百GB,单机内存塞不下,横向扩展就是唯一的出路。

我自己刚接手项目时,团队用的还是“哨兵+主从”模式,主节点64GB内存,高峰期写QPS接近3万,CPU和网络都拉得很满,时不时的慢命令还会把整个实例卡住。当时做扩容要人工改配置、重新做全量同步,成本很高。对比下来,Redis Cluster的分布式设计更符合电商流量模型的特性。

1.1 三种模式的本质差异

对比维度主从复制哨兵模式(Sentinel)Redis Cluster
数据分片无,各从节点全量复制无,各从节点全量复制有,16384个槽位自动分布
写入能力单主节点瓶颈单主节点瓶颈多主节点并行写入
水平扩容只能加从节点提升读能力只能加从节点提升读能力加主节点即可扩展容量和写入
高可用需要人工干预自动故障转移自动故障转移+从节点提升
客户端要求任意Redis客户端任意Redis客户端需要支持Cluster协议
适用规模数据量<单机内存数据量<单机内存数据量远超单机内存

从这张表能明显看出来,前两种方案的核心限制是容量不能水平扩展。电商网站的业务数据不像Session那样可以接受丢失,商品信息、库存快照、价格体系都需要长期缓存,数据量只会越来越大。Cluster的槽位机制把key自动分布到不同节点,写操作也能分散到多个主节点,这才是支撑大规模业务的底座。

1.2 电商场景为什么特别适合Cluster

电商的流量特点有两个:一是“读多写少”,页面浏览、商品详情、搜索结果占绝大多数请求;二是“热点集中”,爆款商品、秒杀活动、大促页面会瞬间聚集几万甚至几十万的并发。

Cluster模式下,你可以把热门商品的数据分片到多个主节点,读写压力自然分散。比如你有6个主节点,一个爆款商品的QPS占20%,打到集群上每个节点只承受3.3%。配合后面要讲的本地缓存和副本读优化,热点问题基本能被压住。

另外,Cluster的在线扩缩容能力也很重要。电商促销节奏有周期性,平时3主3从,大促前扩到6主6从,数据自动rebalance,业务无感知。这一点用过哨兵模式的人都懂,扩节点意味着全量同步和主从切换,稍微一个操作失误线上就得抖几分钟。

2. Ubuntu 20.04上的集群部署全流程:从源码编译到槽位分配

选Ubuntu 20.04是个人习惯,它稳定、资料多,和腾讯云、阿里云的默认镜像兼容性好。虽然apt源里直接装了redis-server,但版本通常偏低(5.x或6.0),有些Cluster调优参数和内存碎片整理功能在高版本才有完整支持,所以我一直坚持源码编译安装。这里用Redis 6.2.14版本做演示,这也是当时生产环境的版本。

2.1 依赖准备与源码编译

先装编译工具链,这一步很多人会忽略,等make报错了才回来补装:

sudo apt update && sudo apt install -y build-essential tcl pkg-config wget https://download.redis.io/releases/redis-6.2.14.tar.gz tar xzf redis-6.2.14.tar.gz && cd redis-6.2.14 make -j$(nproc) sudo make install

编译完成后可以顺手验证一下版本:

redis-server --version

看到v=6.2.14就说明编译好了。这里有个经验:production环境建议下载官方稳定版,不要追最新RC版本。Redis更新节奏快,但大版本之间的配置项和内存管理逻辑有差异,生产环境求的是稳。

2.2 集群节点配置的差异化设置

假设我准备了6台Ubuntu 20.04的服务器,3主3从,分别命名为redis-1到redis-6。每台的redis.conf核心配置如下:

port 6379 protected-mode no daemonize yes pidfile /var/run/redis_6379.pid logfile /var/log/redis/redis.log dir /data/redis # Cluster开关 cluster-enabled yes cluster-config-file nodes-6379.conf cluster-node-timeout 5000 # 持久化 appendonly yes appendfsync everysec # 内存管理 maxmemory 16gb maxmemory-policy allkeys-lru

这里几个配置要重点解释一下:

  • cluster-node-timeout 5000:节点间RPC超时时间。设太短容易误判故障,设太长故障转移太慢。电商场景5秒是比较好的平衡点,配合哨兵检测链路可以在10秒内完成切换。
  • appendfsync everysec:RDB和AOF的写回策略折中,极端情况最多丢1秒数据,对电商缓存场景完全可接受,性能损耗比always低一个量级。
  • maxmemory-policy allkeys-lru:这个在生产环境争议比较大。volatile-lru只淘汰设置了TTL的key,但电商很多缓存key是长期有效的(比如商品基础信息我一般不设过期),单一用volatile-lru会导致不设TTL的key一直占内存,最后内存写满。allkeys-lru让Redis按照LRU算法全量淘汰,实际使用中命中率更均衡。

每台节点的配置基本一样,唯一要区分的是dir目录和日志路径,确保每台机器数据目录不冲突。

2.3 启动所有节点并创建集群

配置文件准备完毕,逐台启动:

redis-server /etc/redis/redis.conf

然后找一台操作机,用redis-cli创建集群:

redis-cli --cluster create \ 10.0.0.1:6379 10.0.0.2:6379 10.0.0.3:6379 \ 10.0.0.4:6379 10.0.0.5:6379 10.0.0.6:6379 \ --cluster-replicas 1

参数含义很明确:前三个IP做主节点,后三个IP做它们各自的一个副本,--cluster-replicas 1就是每个主节点配1个从节点。执行后会询问是否接受槽位分配方案,输入yes并等待。

结束后验证一下:

redis-cli --cluster check 10.0.0.1:6379

正常输出会显示[OK] All 16384 slots covered,每个master负载约5461个槽位,集群状态Healthy。此时任意一台机器执行:

redis-cli -c -h 10.0.0.1 -p 6379 cluster info

看到cluster_state:ok就说明集群已经能对外服务了。

2.4 系统内核参数配合调优

集群跑起来只是第一步,生产环境还必须在每台节点上做内核参数调整,否则流量一起来就各种延迟毛刺。以下三组参数是我在压测时发现影响最明显的:

# 1. 内存分配策略,防止Redis内存不足时被OOM Killer误杀 sysctl -w vm.overcommit_memory=1 # 2. TCP连接队列,高并发下快速握手不丢连接 sysctl -w net.core.somaxconn=65535 # 3. 关闭内存回收导致的延迟抖动 sysctl -w vm.swappiness=0

特别是vm.overcommit_memory,Redis官方在启动时会自动警告如果为0可能导致fork子进程失败。设置成1后,即使物理内存紧张,fork也不会被拒,RDB持久化和BGSAVE才能稳定执行。

3. 缓存效率的核心参数调优:内存、持久化、序列化三板斧

集群部署完,接下来就是本文的重头戏——如何让缓存的命中率更高、读写响应更快、内存使用更合理。我把它分成三块:内存淘汰策略、持久化折中、序列化方式。每一个都是在电商环境下反复压测验证过的。

3.1 内存淘汰:allkeys-lru还是volatile-lru?

前面配置里我用了allkeys-lru,这里展开说下原因。

电商缓存数据分两类:一类必须有TTL(比如验证码、限流计数器、临时秒杀库存),一类希望长期有效(比如商品标题、图片URL、SKU信息)。如果使用volatile-lru,第二类数据就没有淘汰资格,内存一旦满了,新建的key会直接OOM报错。

实际电商场景中,长期有效缓存的比例往往超过50%。我用allkeys-lru的好处是:内存不足以容纳全量热点时,Redis会主动淘汰相对冷的数据,保住真正活跃的商品数据。配合合理的key设计,命中率实测可以达到95%以上。

有人担心allkeys-lru会误淘汰长期数据导致DB压力激增,我的做法是给核心商品数据单独设一个lofter级别的本地缓存,或者把TTL设置成随机24~48小时,让数据的重新构建时间分散开。这个后面讲场景设计时会细说。

3.2 RDB和AOF的取舍:不能只靠默认配置

Redis默认只开RDB快照,这在高频写入的电商场景是不够的——RDB是周期性的,两次快照之间断电,数据直接丢一段时间。AOF每秒刷盘虽然最多丢1秒数据,但对缓存集群来说已经足够,而且重启恢复比RDB更快。

我建议的配置组合是:

# AOF开启,每秒刷盘 appendonly yes appendfsync everysec # RDB可以关掉或保留低频快照做冷备 save 900 1 save 300 10

生产环境我保留了一个每天凌晨3点的RDB快照任务,用于故障后的最坏情况恢复。平时写操作走AOF,aof-use-rdb-preamble yes让AOF文件开头先用RDB格式打底,重写时体积更小,重启加载更快。

3.3 序列化方式:避免JDK序列化的“隐形炸弹”

很多人没意识到,Redis的value序列化方式直接决定内存占用和响应时延。Java技术栈里最常见的两种:

  • JdkSerializationRedisSerializer:JDK自带序列化,体积大、可读性差,序列化后还带一堆类结构描述信息。一个商品对象,JDK序列化出来可能是2KB,而JSON可能只需要500字节。
  • GenericJackson2JsonRedisSerializer:可读性好,体积适中,但JSON字符串的解析CPU开销比二进制格式高。
  • 二进制序列化(Protobuf、Kryo):体积最小、解析最快,但需要额外定义结构。

电商场景qps高、网络带宽有限,强烈建议用二进制序列化。我当时把核心商品缓存从JDK切到Kryo后,同一批数据占用的Redis内存从4.6GB降到1.8GB,响应时延从1.2ms降到0.6ms,效果立竿见影。

下面是序列化方案对比表:

序列化方案体积解析速度可读性适用场景
JDK序列化大慢差不推荐,仅本地测试
JSON中中好通用场景、调试方便
Protobuf小快差跨语言、高并发
Kryo小快差Java项目最佳选择

4. 电商场景下的缓存策略:命中率与热点问题的实战解法

集群部署完成、参数调优到位,剩下最关键的是让缓存真正替数据库扛住压力。这里不讨论纯理论,就说我在商品详情页、搜索结果页和秒杀活动中实际落地过的策略。

4.1 页面缓存与数据缓存的分层设计

电商网页的加载速度优化,不能指望浏览器直接打到Redis。我的这套体系是标准的四级缓存链路:

  1. CDN缓存静态图片、CSS、JS,命中率能做到90%以上;
  2. Nginx层使用Redis做页面片段缓存(比如页面头部、侧边栏广告、推荐位),TTL 30秒;
  3. 应用层用Redis Cluster缓存动态数据(商品信息、库存、价格、用户购物车);
  4. 数据库兜底。

这四层每一层都有明确的TTL和失效机制。比如商品价格变了,会主动删除Redis里的缓存key,并通知Nginx层刷新片段;CDN层设置短TTL,大促时把动态内容降到最低。

这里有一个实际数据:我优化过的一个商品列表页,原来一次请求要打到MySQL,平均响应180ms;走了CDN+Nginx+Redis三层之后,平均响应降到28ms,其中Redis数据缓存的命中率稳定在94%以上。用户感知到的页面加载速度提升非常明显。

4.2 热点key:大促时最怕的隐形杀手

电商的“爆款”问题在Redis面前很矛盾——越是热门的key,越容易被单节点拖垮。我踩过一个大坑:一次秒杀活动,一个SKU的库存key被放到同一个slot,同一时刻几十万个请求全部打到一个节点,节点CPU瞬间打满,整个集群的响应都跟着变慢。

解决热点key有三种实用手段:

  • 复制热点key:将同一个key复制成N份,比如product:123:1、product:123:2……product:123:N,每次读请求随机读其中一个副本,N个副本分散在不同节点上,单key压力变成1/N。
  • 本地缓存兜底:热点key在应用节点上加一层Caffeine或Guava的本地缓存,TTL设5秒。即使Redis集群某个节点抖动,本地缓存也能扛住几波请求,给Redis恢复争取时间。
  • 散列标签:如果热点key可以和业务维度拆开,比如按商品品类拆成多个key,写入端分片后读端聚合,也能避开单一slot。

需要注意的是,复制key的副本内存占用会成倍增加,只适合确定的热点。我的经验是,对秒杀商品、爆款单品做副本操作,普通商品不碰,否则内存会被搞爆。

4.3 缓存穿透、击穿、雪崩的标准应对

这三个问题是电商缓存永恒的主题,直接说我的标准答案:

  • 穿透:查询一个数据库中不存在的数据(比如下架商品ID),Redis没有,每次都打到DB。我的办法是:把空结果也写进Redis,TTL设30秒;同时用布隆过滤器在网关层拦截。商品ID有规律,构建一个包含全量商品ID的布隆过滤器非常划算,可以拦截99%的非法ID请求。
  • 击穿:热点key过期的瞬间,大量请求同时打到DB。我的办法是:互斥锁或分布式锁,只让一个请求去DB重建缓存,其他请求短暂等待或降级。Redis Cluster下用SETNX实现分布式锁,注意设置过期时间防止死锁。
  • 雪崩:大量key同一时刻过期,导致DB瞬间压力。我的办法是:过期时间加随机数,让每个key的过期时间散开;同时大促前对核心缓存做预热,把一份备份数据提前写入集群,即使部分key过期,备份Key也能顶上。

这几个方案组合使用后,我在上线期间再没遇到过DB被打爆的情况,最严重的一次大促,MySQL的CPU峰值也只有40%,这在以前是想都不敢想的。

5. 集群运维监控与踩坑复盘:那些不压测发现不了的问题

部署和优化完成,运维就变成日常的头等大事。Redis Cluster看起来能自动故障转移,但一些隐藏问题不亲身体会,完全不会意识到它们有多麻烦。这一节把我踩过的坑和解决方案全部说出来。

5.1 监控指标:别只看QPS,这些指标更重要

集群健康度不能只看QPS和CPU占用。我日常巡检的指标有这几项:

  • 命中率(keyspace_hits/keyspace_misses):电商场景低于90%就要警惕了,说明缓存设计有问题。
  • 内存碎片率(mem_fragmentation_ratio):超过1.5说明碎片严重,需要开启自动归档整理。
  • 慢查询日志:通过SLOWLOG GET 10看是否有慢命令,单个命令超过100ms就必须处理。
  • 网络和延迟抖动:用redis-cli --latency从客户端侧测实际响应延迟,不能只看服务器自己报的。

启用内存碎片自动整理配置:

activedefrag yes active-defrag-ignore-bytes 100mb active-defrag-threshold-lower 10 active-defrag-threshold-upper 100

这个配置上线后,实例运行一个月的内存碎片率从1.8降到了1.2,稳定很多。

5.2 大key与热key的排查:redis-cli是最大的帮手

集群环境下大key是致命的:一次GET返回几十MB,会阻塞单线程模型下所有后续请求。我用以下命令定期扫描:

redis-cli -c -h 10.0.0.1 -p 6379 --bigkeys

这个命令会输出所有大key的类型和有问题的key,发现后拆分或延迟处理。比如用户购物车,一个用户可能塞几百个商品ID,存成一个List,有时候一个key就有几MB。我后来改成每个用户拆成多个key,按活跃度缓存,排行榜、购物车这类场景都适用。

热key排查通过redis-cli --hotkeys查看,但注意这个命令只在特殊编译版本里可用,实际生产环境更方便的办法是使用redis-cli --stat观察实时状态,再配合业务日志中的访问频次统计。

5.3 故障转移演练:不能等到大促才临阵磨枪

集群的高可用性不能只看配置,必须实际演练。我第一次做故障转移演练时,手动redis-cli -h 10.0.0.1 -p 6379 DEBUG sleep 30模拟节点假死,结果发现:

  1. 从节点接管后,应用层客户端没有自动刷新路由表,导致连接失败率一度达到5%;
  2. 触发切换的那几秒,集群整体写入失败率比较高。

后续优化方案:应用层引入JedisCluster或LettuceCluster的连接自动刷新机制,配置topology-refresh-period为5秒;同时在超时重试逻辑里加入maxAttempts=5和退避策略,客户端自动感知新主节点。

演练后,真正的故障切换时间从30秒减少到10秒以内,大促前我们会做三次这样的演练,确保万无一失。

5.4 性能压测:没有数据支撑的优化都是心理安慰

优化完之后一定要做压测,拿数据说话。我用redis-benchmark测试集群基础吞吐,配合实际业务请求模拟脚本:

redis-benchmark -h 10.0.0.1 -p 6379 -t get,set -n 1000000 -c 500 -P 64

这个命令模拟100万次get和set请求,500个并发连接,64个管道请求。Cluster多节点下,实测GET吞吐能到18万QPS,SET吞吐大概11万QPS,单命令平均延迟0.4ms。这个数据对支撑日均千万级PV的电商网站是足够的。

压测还有一个重要作用,是发现连接池和线程池配置是否匹配。应用端JedisPool如果maxTotal设置太小,集群吞吐再高也会被客户端卡住,这属于典型的“木桶效应”。我用500并发压测时发现,默认JedisPool(maxTotal=8)直接把集群打残了,调整到maxTotal=200后才把Redis的性能真正释放出来。

5.5 几个高频问题的快速排查清单

问题现象可能原因解决方案
Redis响应偶尔卡顿,CPU不高大key扫描/重写AOF/内存碎片整理关闭自动碎片整理或拆分大key
集群切换后部分请求失败客户端路由表过期开启topology-refresh,增加重试
内存只升不降碎片率过高,或有大对象持续写入activedefrag + 排查大key
命中率持续在80%以下过期时间过短,或key设计不合理加长TTL、增加本地缓存层
写入时CROSSSLOT报错多个key的哈希标签不一致使用hash tag确保同slot

6. 我个人的最终实践体会

集群搭建不难,真正产生价值的是后续持续的观察和调整。我们现在的架构稳定经历了三次大促压测验证,页面平均加载时间从1.8秒优化到0.4秒以内,Redis集群的命中率保持在94%左右,MySQL的流量下降超过70%。这个结果不是单靠Redis一台软件搞定的,而是选型合理、参数对路、场景策略到位共同作用的结果。

最后分享一个容易被忽视的小技巧:大促前三天,我会写一个脚本把核心缓存批量刷新一遍,让所有热点key有一个相对接近的过期起点。这样整个集群的内存使用率平滑上升,比大促当天冷数据抢占内存靠谱得多。另外,生产和测试环境严格隔离,别拿测试的压测数据当生产指标参考,这是很多团队配置相同但效果差别巨大的根源。

如果你正准备给电商站点上Redis集群,按这篇文章的路径走一遍基本能跑通。遇到问题不要慌,从监控指标倒推,大概率都能定位到配置或策略问题。祝大家的缓存层都稳如老狗。

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

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

立即咨询