先说一句大实话Linux 上折腾 Redis 升级大多数时候不是 Redis 本身有多难而是我们自己在流程上太自信。我见过太多拿着 5.x 的老实例直接替换 7.x 二进制最后不是配置项不兼容就是 RDB 文件格式载不进去或者主从复制链路直接断掉。这篇文章不打算写成一个标准 API 文档式的教程而是把我这些年实际在 Linux 环境里操作 Redis 升级的完整思路、实操步骤、踩坑记录整理出来。尤其是那几个听起来很玄学的问题比如“gcc 升级后为啥还是旧版本”、“配置看起来没问题为什么启动失败”我会把背后的原因一次性说透。如果你是那种已经被业务压得喘不过气、不得不给老 Redis 做版本升级的运维或后端开发本文可以把你的操作风险降到最低。内容会覆盖跨大版本、单机与多实例、主从复制与集群场景以及升级完成后的验证套路尽量做到可以直接照着画瓢同时也够你在评审会上跟别人解释清楚为什么这样操作。1. 升级前必须想清楚的三件事1.1 Redis 版本差异并不只是数字很多同事在评估升级时习惯只看“能不能装”却忽略了 Redis 各个大版本之间的行为变化。这个忽略在线上是非常致命的。Redis 3.x 时代大家最常用的是哨兵和集群雏形4.x 开始引入了模块系统和 PSYNC2主从复制的断点续传能力大幅提升5.x 带来了 Stream 数据类型算是把消息队列的场景抓稳了6.x 引入了 ACL 权限控制、SSL 支持和 IO 多线程这也是很多人从老版本往上跳的一个重要理由到了 7.xAOF 文件的重写机制、命令执行系统、Redis Functions 等都有了不小的改动。这些差异意味着如果你从 4.x 直接升到 7.x不只是换一个二进制那么简单。有些老参数名被彻底废除有些命令的返回结构变了还有的客户端库需要配合升级。比如 Redis 6 开始默认使用 RESP2 协议但兼容 RESP1老客户端基本能继续用但如果你的客户端连接池写法非常古老建议先在测试环境把读、写、订阅全部过一遍。还有一个容易轻视的点不同大版本生成的持久化文件并不是完全“互通”的。RDB 文件的格式总体上保持向后兼容也就是新版本可以正常读取旧版本生成的 RDB但反过来不一定成立旧版本很难读新版本生成的 RDB。AOF 文件稍微好一点因为它是命令追加但遇到新命令或者新的编码方式旧版本同样可能执行失败。因此升级之后不要轻易用旧二进制去回滚这一点后面我会专门展开。1.2 别急着动老实例先摸清拓扑拿到一台服务器就急着下载 Redis、编译、替换这是升级事故里的头号操作。我自己的习惯是先花 20 分钟把现网拓扑盘清楚弄清楚下面几个问题。这台服务器上到底跑着几个 Redis 实例是单实例还是多个端口共存有没有主从复制关系是否有哨兵在监控是集群模式还是普通模式持久化策略是什么RDB 和 AOF 是否同时开启业务连接用的是长连接池还是每次短连接客户端集中在哪些语言盘清楚这些升级方案才谈得上有针对性。我这里通常会用几条命令快速确认比你翻文档要直观得多。# 查看本机监听端口和对应进程 ss -lntp | grep redis # 查看指定实例的角色与主从信息 redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning INFO replication # 查看持久化状态 redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning INFO persistence # 查看当前配置中与存储路径、端口相关的关键参数 redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning CONFIG GET dir redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning CONFIG GET port这一步的核心价值在于你要明确每次升级操作波及的范围。如果只是单机缓存实例升级影响窗口可能只有几十秒如果上面挂着主从复制和业务流量那就必须把切换路径设计好绝对不能直接kill进程然后替换。1.3 回滚计划比升级计划更重要我见过太多团队做升级方案时写得天花乱坠但问“失败了怎么办”答不上来。Redis 这种有状态服务升级失败的回滚难度远高于无状态应用所以回滚计划必须写在升级计划前面。回滚计划要包含三块内容完整的配置备份、数据文件备份、原版本二进制保留。三块缺一不可。配置备份不是只cp redis.conf就完了还要把 systemd 的 service 文件、可能用到的 env 文件、启动脚本一起备份。数据文件备份则要根据实例目录来常见的有/data/redis、/var/lib/redis、/usr/local/redis等你别想当然一定要通过CONFIG GET dir查清楚。我常用的备份流程是这样# 1. 保留原配置目录 cp -a /etc/redis /etc/redis.bak.$(date %Y%m%d%H%M) cp -a /etc/systemd/system/redis.service /etc/systemd/system/redis.service.bak.$(date %Y%m%d%H%M) # 2. 触发 BGSAVE生成一份最新 RDB redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning BGSAVE # 3. 等 BGSAVE 完成后再备份数据目录 redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning INFO persistence | grep rdb_bgsave_in_status这里提醒一句不要看到 BGSAVE 返回 OK 就直接拷贝文件。BGSAVE 是异步的返回 OK 只代表任务排队了。要确认rdb_bgsave_in_progress为 0且rdb_last_bgsave_status为 ok再对数据目录做备份不然可能拷到一半的文件。反正我吃过这个亏备份出来的 RDB 是不完整的好在没真用到回滚。2. 升级实操全流程从备份到切换2.1 备份不是简单 cp要做可恢复验证很多文章会告诉你备份好文件就能升级我觉得这是不够的。备份文件只有验证过能恢复它才算有效备份。所以我在备份阶段会顺手做一件很关键的事用旧版本的redis-check-rdb工具检查 RDB 文件完整性。# 找到旧版本自带的检查工具 /usr/local/redis/bin/redis-check-rdb /var/lib/redis/dump.rdb # 如果是老版本路径可能直接叫 redis-check-rdb如果没有检查工具至少要确认文件大小在增长后趋于稳定并且能用xxd或strings看到 RDB 文件头部的 Redis 版本标识。只有确认数据文件本身没有损坏升级操作才有底气。这一步做得越充分后面升级时心里越踏实。还有一点容易被忽略AOF 文件也要做静态备份。虽然新版本可以重放 AOF但既然升级是一个变更窗口就该把 AOF 和 RDB 都留在原地不要动然后整个目录做 tar 压缩备份。cd /var/lib tar -czf /backup/redis-data-$(date %Y%m%d%H%M).tar.gz redis有些人觉得数据量太大不想 tar就直接cp -a。cp 也不是不行但要注意如果实例还在持续写入拷贝出来的文件可能处于“中途状态”。所以最稳妥的姿势是先 BGSAVE再临时停写或直接进入维护窗口然后拷贝。如果是单机缓存场景可以在凌晨低峰批量操作如果是业务实时性要求极高的场景就得靠主从切换来缩短影响窗口。2.2 源码安装与 GCC 的“隐性”问题Linux 上安装 Redis 的主流方式有三种源码编译、系统包管理器安装、容器化部署。生产环境里我接触到的老实例很多是源码编译部署的所以要聊升级就绕不开源码编译这道坎。去官网下载指定版本的源码包是常规操作。比如升级到 7.x可以这样下。wget https://download.redis.io/releases/redis-7.2.5.tar.gz tar -xzf redis-7.2.5.tar.gz cd redis-7.2.5编译前先确认依赖装了没有。CentOS/RHEL 系列需要gcc、make、openssl-develUbuntu/Debian 需要build-essential、libssl-dev。# CentOS/RHEL yum install -y gcc make openssl-devel # Ubuntu/Debian apt install -y build-essential libssl-dev编译命令我习惯加两个选项一个是BUILD_TLSyes一个是MALLOClibc。前者是为了支持 TLS 连接后者是为了避免用 jemalloc 时在新老系统之间出现兼容差异。make distclean make BUILD_TLSyes MALLOClibc -j$(nproc) make install PREFIX/usr/local/redismake distclean这步很重要。如果之前编译过其他版本不清干净就重新编译经常会出现一些奇怪的链接错误。编译完成后确认版本号。/usr/local/redis/bin/redis-server --version这里就非常容易遇到“gcc 升级后为啥还是旧版本”这类问题。很多人下载了新版本的 gcc但没有重新登录终端也没有修改 PATH结果编译时还是调用了/usr/bin/gcc这个软链接指向的旧编译器。一句话总结你升级了 gcc 不等于当前的 shell 就真的在用新 gcc得先which gcc看清楚。which gcc gcc --version如果显示路径在/usr/local/bin/gcc但版本还是旧版多半是软链接没有指对。如果which gcc显示/usr/bin/gcc那说明系统默认还没切到新版编译工具。此时要么修改 PATH 的顺序要么用update-alternatives --config gcc切换默认要么重新登录终端。生产环境不建议直接删除系统自带 gcc容易出现连锁依赖问题。编译完成并不代表万事大吉。真正到你启动新版本实例时才会暴露很多配置兼容问题所以下一节我会把启动验证和数据加载一起讲。2.3 启动新版本并做数据校验拿到新二进制后先不要直接替换生产实例。我的习惯是找一台测试机或者在同一台服务器上先起一个临时端口实例把数据加载过程完整跑一遍。比如旧实例在 6379 端口我就在 6399 端口先启动新版本加载同一份 RDB 看是否报错。/usr/local/redis/bin/redis-server /etc/redis/redis.conf --port 6399 --daemonize yes如果旧配置里有明显的版本不兼容项启动时 Redis 会直接报错并退出或者打印 warning。这个时候千万不要忽略一条一条看。启动后立刻检查是不是真的把 RDB 加载进去了。redis-cli -p 6399 -a $REDIS_PASS --no-auth-warning DBSIZE redis-cli -p 6399 -a $REDIS_PASS --no-auth-warning INFO persistenceDBSIZE 和旧实例对比一下如果数量级差太远说明加载的数据不完整或者加载的根本不是同一份文件。还要检查rdb_last_load_status是否为 ok。这一步能拦住极大部分升级事故。验证完数据之后临时实例就没用了。可以SHUTDOWN NOSAVE关掉不要留下新格式的 RDB 文件污染旧数据目录。3. 升级路径与细节单机、多实例、集群怎么取舍3.1 单机升级停写、替换、再验证如果业务允许短时间停止写入单机实例的升级其实是最朴素的备份、停写、关实例、换二进制、启动、验证、恢复流量。先让实例不再接收写入。这里不建议直接杀掉进程因为 Redis 的 RDB 保存和主从复制状态可能还没写盘直接 kill -9 会丢掉部分数据。正确做法是给一个缓冲时间来触发保存。# 临时停写让业务切走 redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning CONFIG SET requirepass $OLD_PASS redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning BGSAVE # 查看保存状态 redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning INFO persistence确认rdb_bgsave_in_progress为 0 且rdb_last_bgsave_status为 ok 后再执行SHUTDOWN。redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning SHUTDOWN服务停止后把旧二进制目录改名再把新二进制目录放上去。然后通过 systemd 或原启动脚本拉起服务。mv /usr/local/redis /usr/local/redis.old.$(date %Y%m%d%H%M) mv /usr/local/redis.new /usr/local/redis systemctl start redis启动后不能只看进程活着要立刻对关键 key 做抽样校验。我通常会写一个小脚本从旧实例里导出 20 个随机 key 的 TTL 和类型再在升级后的实例里对照。TTL 是否接近、数据类型是否一致、热点 key 是否存在这几项能帮你快速发现数据异常。3.2 多实例场景一台机器上几十个 Redis 实例怎么办很多服务器上并不是只跑一个 Redis而是按业务拆分可能同一台机器上有 6379、6380、6381 三个实例甚至更多。这种场景下如果一次把所有实例全部升级风险窗口会成倍放大因为每个实例的数据量、连接数、持久化配置都可能不同。我的做法是分批升级一个实例彻底验证完再动另一个。批次之间留出观察时间不要贪快。同一台机器多实例升级要特别注意配置文件里的绝对路径和 PID 文件路径。比如旧配置里写死了pidfile /var/run/redis/redis_6379.pid新版本启动时如果目录权限不对就会启动失败。另外多个实例共用一个 Redis 二进制目录没有关系但数据目录、日志目录必须独立。如果你用了 systemd 模板服务比如redis6379升级后最好检查ExecStart是否还指向旧版本路径。这里分享一个实用命令可以批量对比各个实例的 DBSIZE快速发现哪个实例升级前后数据量偏差大。for port in 6379 6380 6381; do echo port: ${port} redis-cli -p ${port} -a $REDIS_PASS --no-auth-warning DBSIZE done多实例场景下我强烈建议优先采用“新版本作为从库追平后切换”的方式而不是一台机器上直接逐个替换。这种方式对业务影响小而且每完成一个实例的切换都能在实际流量下验证新版本稳定性再继续下一个。3.3 集群和主从复制别让数据同步变成灾难如果 Redis 实例上开启了主从复制升级策略就不能太粗暴。直接升级主节点时如果从节点还是旧版本新主节点生成的 RDB 或复制流可能让旧版从节点无法正确处理轻则同步断开重则从节点数据全量重拉失败。正确思路是让新版本先行再做角色切换。假设当前拓扑是 A 为主节点B 为从节点。先在另一台物理机或者同机新端口上部署一个新版本实例 C把它作为 B 或 A 的从库挂上去。# 在 C 的 redis.conf 中写入 replicaof A_IP 6379然后观察 C 的复制进度。重点看两个 offset 是否追平。redis-cli -p 6380 -a $REDIS_PASS --no-auth-warning INFO replication | grep master_repl_offset redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning INFO replication | grep master_repl_offset当两个值一致说明 C 已经从 A 全量同步完成并追平了增量。此时可以把业务连接从 A 切到 C或者让 C 成为新的主节点。如果用的是哨兵需要把监控对象重新配置如果用的是 DNS 或 VIP直接把漂移逻辑切到 C 即可。这种升级方式最大好处是即使 C 在承接流量后表现异常还能马上把流量切回 A因为 A 还在运行数据也没有被新版本污染太多。当然回滚时要注意A 作为旧版本能不能继续承载新版本写入过的数据取决于有没有发生无法兼容的命令写入。所以一旦切换完成业务稳定后再判断是否清掉旧节点。集群模式下的升级逻辑也类似。Redis Cluster 官方允许滚动升级顺序是先升级从节点再对从节点执行CLUSTER FAILOVER让它接管主节点角色然后升级原来的主节点。整个过程要控制同一时间集群内不同版本的节点数量越少越好避免跨版本运行太久。4. 升级现场踩过的坑与排查经历4.1 GCC 版本“升了但没升”命令路径问题的真相“gcc 升级后为啥还是旧版本”这个问题在 Redis 编译过程中出现频率极高尤其是在 CentOS 7 这种自带老 gcc 的环境里。明明按照网上的教程下载了 devtoolset 或者手动编译了新 gcc结果gcc --version还是显示 4.8.5很多人到这里就开始怀疑系统坏了。实际上不是系统坏了而是你编译 Redis 时用的 gcc 并不是你升级的那个 gcc。Linux 下执行命令时系统会按照 PATH 环境变量里的目录顺序查找可执行文件。如果你新安装的 gcc 在/usr/local/bin而 PATH 里/usr/bin排在前面那系统找到的仍然是/usr/bin/gcc也就是旧版。排查思路可以这样# 看当前实际调用的 gcc 在哪个路径 which gcc # 查看两个路径下的 gcc 版本 /usr/bin/gcc --version /usr/local/bin/gcc --version # 查看 PATH 顺序 echo $PATH如果实际还在调用旧路径有两个处理方式。一种是临时在编译环境里调整 PATH把新 gcc 放前面。另一种是用update-alternatives设置系统级默认 gcc。但这两种方式都要谨慎尤其不要直接删/usr/bin/gcc因为系统里很多底层组件依赖它一旦删除很可能引发连锁问题。还有一类情况是装了 devtoolset但只有执行scl enable devtoolset-11 bash之后才生效。如果你开的是新终端没有主动 enable那 shell 里的 gcc 自然还是旧的。这个不是玄学而是 Linux 环境变量作用域的问题。4.2 老配置项在新版本解释不是报错就是要清理Redis 大版本升级后老配置文件里最容易出问题的就是配置项名称变化。很多配置项在 6.x 之后做了重构比如slave-read-only这类带 slave 字样的配置逐步被replica-read-only取代。旧版本可能只是打印一个 deprecated 提示但某些新版本对无法识别的直接配置会拒绝启动。所以升级前不要急着照搬旧的 redis.conf我的建议是先在新版本里不带配置启动一次确认基本程序能跑。/usr/local/redis/bin/redis-server --port 6399 --daemonize yes然后再把旧 redis.conf 通过include或者直接复制的方式放进去观察启动输出。遇到 warning一次处理掉遇到 error针对性改成新参数。常见的几个调整点包括slave 前缀参数统一替换成 replica 前缀。过时的maxmemory-policy取值比如某些老版本里自定义的淘汰策略名称。appendonly的默认值差异新版默认不开但老配置如果显式写了一般不会冲突。涉及密码的配置新版更推荐使用 ACL 文件管理用户权限不再建议直接在命令行里传递密码。4.3 内存参数和系统参数导致的新版本运行时异常有时候升级后进程能启动数据也没问题但运行一段时间后出现响应变慢或者内存异常升高。这类问题很多不是 Redis 自身的 bug而是操作系统层面的参数没跟上。最常见的是vm.overcommit_memory没有设置。Redis 在做持久化时会 fork 子进程如果系统内存 overcommit 策略不允许fork 就可能失败进而触发写入失败或者伪延迟。另一个高危坑是透明大页 THP。Redis 官方明确建议把 THP 关闭否则可能出现明显的延迟抖动尤其在写多读少的场景下。我一般在升级后都会检查这两项。# 查看 overcommit 设置 cat /proc/sys/vm/overcommit_memory # 查看 THP 设置 cat /sys/kernel/mm/transparent_hugepage/enabled如果 overcommit_memory 不是 1建议临时调整并写入/etc/sysctl.conf。THP 如果不是 never也要通过 systemd 的 unit 文件或者内核参数改成 never。这些系统参数看起来跟 Redis 升级没有直接关系但恰恰是它们决定了新版本能不能跑得稳。升级时顺手把这些东西一起校准可以减少很多后半夜被叫起来的概率。4.4 回滚能力强过了头也可能是坑回滚计划重要但回滚操作本身也要讲策略。最典型的错误是升级后运行了几天新版本已经写了不少新格式的 RDB 和 AOF此时想回滚到旧版本直接拿旧二进制去加载新数据文件结果加载不了。原因我在前面提过RDB 和 AOF 的格式在向后兼容时比较友好但在向前兼容上经常是做不到的。新版本写入的文件旧版本未必读得懂。所以回滚时不要偷懒不要直接把旧实例的数据目录替换为新实例正在用的目录。正确做法是保留现场把新版本实例先停掉或者调整业务写入。把备份的旧目录完整恢复回去。确保旧版本加载的是旧备份或者旧格式数据而不是新版本改过的文件。回滚后再做一次数据校验确认 DBSIZE 和关键 key 都在。如果回滚前已经积累了大量新写入的数据单纯恢复旧目录会丢失这部分增量。这时候要提前跟业务方确认增量数据是否可以接受丢失如果不能接受就要考虑从新版本导出数据再导入旧版本但这通常工程量不小。所以我才一直强调回滚计划不是写过场是要真的能落地的。4.5 升级完成后的验证与后续动作新版本跑起来不代表事情结束了。我通常会在升级后的第一个小时、第一个小时之后、以及第二天各做一次检查。刚启动时重点看日志有没有 error 和 fatal以及主从同步状态是否正常。redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning INFO replication redis-cli -p 6379 -a $REDIS_PASS --no-auth-warning INFO stats | grep -E total_error_replies|total_commands_processed运行一段时间后再看内存碎片率、命中率、连接数这些指标。碎片率过高说明内存分配器可能和之前不一致如果是 jemalloc 和 libc 混用导致的情况要考虑是否需要调整 MALLOC 重新编译。升级后的性能对比我建议用redis-benchmark或者业务侧的真实请求来测别凭感觉。同样的压测参数在升级前后各跑一遍拿到 P99 和吞吐量再做判断。redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 100000 -t set,get -r 100000 -d 128另外如果监控系统里有 redis_exporter 或者自研采集脚本注意 INFO 输出结构可能变化指标字段也可能改名。升级完不去更新监控等告警炸了再回头看这是最常见的“升级后遗症”。4.6 一点个人心得敲完这么多内容我想把你拉回一个更实际的视角。Redis 升级这件事在 Linux 上并不存在所谓的“标准答案”更多时候是在权衡停机窗口、数据安全、性能表现和运维成本。如果你问我个人偏好我会毫不犹豫地说有能力用“新实例做从库、追平后切换”的绝不用原地替换能分批做的绝不全量同时做能提前验证的绝不到现场赌运气。我自己在升级过程中最受益的一个习惯是每一条执行的命令都有日志每一条修改的配置都有备份每一个步骤都写清楚“失败了怎么办”。这套习惯救过我太多次也让我在升级完成后可以放心睡个整觉。如果你现在正准备给生产环境 Redis 做升级不妨把本文当作一张检查清单来看。先从最基础的数据备份开始一步步来把风险点提前排掉。真正高水平的运维不体现在升级操作有多快而体现在出问题时你能多冷静地回到备份和回滚路径上。希望这篇文章能让你少踩几个坑。