
1. 先弄清楚同是 Redis为什么非要升级不可做运维这些年我见过太多“能跑就不动”的服务器Redis 更是重灾区。很多团队从某个 LTS 版本装上之后就再也没碰过直到遇到内存碎片率高得离谱、主从切换卡顿、或者发现线上被扫描到某个高危漏洞才想起来问一句“Redis 要不要升级”。先说结论绝大多数在 Linux 上运行的 Redis 实例都值得升级到 6.x 或 7.x 主线版本前提是你先把下面这些事想清楚。Redis 的版本演进不是挤牙膏。3.x 时代引入集群4.x 引入模块系统与混合持久化5.x 解决了大量 AOF 重写阻塞问题6.x 带来多线程 IO、ACL 权限控制、RESP3 协议7.x 则在性能、内存效率、集群管理上又做了一轮大手术。每一代升级都不是单纯“修复了若干 bug”而是直接影响你在生产环境里能使用哪些能力。举个例子6.x 之前的 Redis 没有 ACL所有客户端共用一条权限通道一旦某个应用的 key 被误删你根本查不出是哪个连接干的。升级到 6.0 之后每条业务线独立账号、独立命令白名单故障定位直接从“猜”变成了“看”。这类提升不升级是体会不到的。还有一个更现实的理由安全补丁。Redis 历史上出过好几轮严重的远程命令执行漏洞官方往往只对最近两个大版本提供修复。你还在用 5.0 当宝等于把大门敞着。所以这篇我直接把我自己在 Linux 上升级 Redis 的完整做法、踩过的坑、以及翻车后怎么回滚都写出来。适合三类人线上 Redis 已老旧、正准备升级的运维同学在个人服务器上折腾、想尝鲜新版特性的开发者以及在云主机上部署过 Redis、但从来没考虑过版本管理的朋友。2. 升级前的体检别一上来就动数据我第一次给生产环境升级 Redis 时吃过亏。当时一看新版本发布了直接编译安装、替换二进制、重启服务结果老配置加载报错旧 RDB 文件版本不认线上缓存全部重建把数据库打出了慢查询告警。从那之后我总结出一句话Redis 升级真正的难点不是装新版而是让旧数据无缝适配新版。2.1 摸清家底版本、数据量、架构一个都不能漏先上一组命令把当前环境完整扫描一遍# 查看当前 Redis 版本 redis-cli info server | grep redis_version # 查看持久化配置 redis-cli config get save redis-cli config get appendonly redis-cli config get dir redis-cli config get dbfilename # 查看当前内存与数据集规模 redis-cli info memory | grep used_memory_human redis-cli info keyspace这里有个很容易忽略的点dir配置决定了 RDB/AOF 文件落盘路径。升级过程中如果新版本的启动脚本或 systemd 服务里 WorkingDirectory 变化了可能导致 Redis 在启动时找不到旧数据文件直接当空实例初始化。所以判断数据是否真的“迁移成功”别只看服务起来了还得用keyspace里的 key 总数和dbsize做对比。架构信息同样重要单机 Redis重点考虑备份、回滚、服务停机窗口主从复制先升级从库再手动触发主从切换最后升级原主库Redis Cluster需要逐个节点滚动升级且要保证槽位数据完整Sentinel 高可用运维涉及选主逻辑和配置兼容我见过有人单机 Redis 也先备份、再直接杀进程等新版本起来后keyspace只有预期的一半半天才发现是因为dbfilename不一致导致的。这类低级错误体检阶段就能发现。2.2 数据备份的两种思路物理备份与逻辑重放升级前备份我一般做双重保障。第一层RDB 文件物理复制。找到dir目录把 dump.rdb 拷贝一份到独立目录并且确认文件大小大于 0mkdir -p /data/redis_backup_$(date %F) cp /data/redis/dump.rdb /data/redis_backup_$(date %F)/注意直接复制运行中的 RDB 文件可能抓到一个不完整快照。最稳妥的方式是先通过redis-cli触发一次 BGSAVE等SAVE状态变为正常、RDB 文件时间戳更新后再复制redis-cli bgsave redis-cli info persistence | grep rdb_last_bgsave_status第二层AOF 文件 增量指令。如果appendonly yes最好把 AOF 文件也一起备份。AOF 文件可能比 RDB 大得多但恢复时更精准能覆盖到最后一次写操作。升级前的最后一个小时如果有高频率写入哪怕 RDB 丢了最近一段增量AOF 也能帮忙补上。我还要多说一句如果条件允许主从架构下更优雅的数据保障方式是“起一个临时从库”。让临时从库同步到最新位点后把 RDB/AOF 从临时从库拷贝出来。主库全程不参与备份完全不影响线上写入。这里的关键是注意从库要和主库用同一个 bind 网段临时从库的 repl-timeout 设置合理避免同步中断。2.3 配置兼容性体检新版启动不了九成是配置问题升级前把现有配置文件和新版本默认配置做一次 diff非常有必要。Redis 各个版本之间配置项变动不小5.x → 6.x新增aclfile、io-threads、tls-port等配置旧配置基本能跑6.x → 7.x去掉了protected-mode的部分默认行为maxmemory-policy的某些选项语义有调整老配置里有save 这种写法需要特别确认模块相关如果加载了第三方模块如 ReJSON、BloomFilter必须确认该模块是否兼容新版 Redis 的模块 API有个笨但有效的办法在新服务器上解压新版把旧配置复制过去启动一遍看报错。没有新服务器就用源码目录里自带的 redis.conf 做对照。我通常直接跑一次redis-server /path/to/old.conf如果控制台没有 ERROR 级别日志基本就成功了一半。不过我不建议直接沿用旧配置建议按新版本语义重新过一遍几个关键项protected-mode是否保持 yesappendonly是否要开启7.x 建议开多任务写场景收益明显save策略是否匹配你的数据量maxmemory是否设置避免升级后内存超卖导致 OOMlogfile是否和 systemd 日志监控冲突2.4 客户端兼容性升级后连接不上先别怀疑 Redis很多人升级完 Redis 才发现应用层炸了。这锅通常是客户端的。如果业务用的是老版本 Jedis2.x 早期、Lettuce 5.x 之前、或redis-py3.x 以前版本连接新版 Redis 6 以上时可能出现协议、密码、失败重连等问题。最典型的是新版 Redis 默认开 RESP2 兼容但你显式切到 RESP3老客户端直接无法处理返回格式。我建议升级前把项目里的 Redis 客户端依赖统一向最新稳定版靠拢和 Redis 服务端升级放在同一个发布窗口内。分布式锁这种强依赖命令语义的场景更要做一次完整回归测试。比如SET key value NX EX在 6.x 之后的返回语义更严格某些老客户端会把返回OK和返回nil的判定弄反甚至把成功写成失败。3. 两种升级路线二进制替换和编译安装怎么选有基础的读者可以直接跳到 3.2 的实操流程拿不准的先把 3.1 看完。3.1 我为什么不推荐发行版源安装Ubuntu 的 apt 和 CentOS 的 yum 源里确实有 Redis但发行版仓库普遍滞后。比如某段时间 Ubuntu 20.04 的官方源里 Redis 还停留在 5.0.x而官方早已发到 7.x。除非你用的是 Redis 官方维护的ppa:redis/redis或第三方维护较快的源否则通过系统源升级很容易“升了个寂寞”。另外用系统包管理器安装 Redis你很难控制编译参数。比如在 aarch64 架构上想开启某类指令级优化发行版二进制不支持或者你想把 Redis 编译成静态链接版降低对系统库的依赖也只有源码编译能做到。我个人经验是Redis 升级优先用官方 release 的二进制包或源码编译两者都靠谱看你的环境更信哪一种。编译只多花几分钟但换来的是完全可控。从安全和可控角度我还建议在官方发布页面下载时校验 sha256 校验和。这一步极其重要但常被忽略。哪怕是官方包也建议养成校验习惯避免在下载后被篡改。命令如下curl -L -o redis-7.2.5.tar.gz https://download.redis.io/releases/redis-7.2.5.tar.gz echo xxxxxxxxxxxxxxxxx redis-7.2.5.tar.gz | sha256sum -c -如果你所在服务器访问外网慢可以提前把 tar.gz 包下载到内网文件服务器再批量分发。批量升级的场景我很推荐这种方式配合同步工具如 rsync能很快完成多机替换。3.2 编译安装完整流程以 7.2.x 为例这套流程我在 CentOS 7/8、Ubuntu 20.04/22.04 上都实测过直接照抄基本不会翻车。第一步安装编译依赖# Debian/Ubuntu apt update apt install -y build-essential pkg-config libjemalloc-dev libssl-dev # CentOS/RHEL 系列 yum install -y gcc make tcl jemalloc-devel openssl-devel这里重点说下 libjemalloc。Redis 官方默认使用 jemalloc 作为内存分配器编译时如果找不到会在 config 阶段自动回退 libc malloc。这个回退平时看不出问题但高并发、大内存场景下碎片率和性能差异很明显。所以我总是手工指定MALLOCjemalloc编译参数。第二步编译安装到独立目录不覆盖老版本我不建议直接把新版二进制覆盖到/usr/local/bin/redis-server。虽然版本管理上看不太出来但一旦需要回滚你就懵了。推荐安装到一个版本号目录里比如mkdir -p /usr/local/redis/redis-7.2.5/src cp src/redis-server /usr/local/redis/redis-7.2.5/src/ cp src/redis-cli /usr/local/redis/redis-7.2.5/src/ cp src/redis-sentinel /usr/local/redis/redis-7.2.5/src/ cp src/redis-benchmark /usr/local/redis/redis-7.2.5/src/再用软链接把默认路径指向新版ln -sfn /usr/local/redis/redis-7.2.5/src/redis-server /usr/local/bin/redis-server ln -sfn /usr/local/redis/redis-7.2.5/src/redis-cli /usr/local/bin/redis-cli这样一来既有新版本可用又在/usr/local/redis/下保留了老版本目录回滚时只需要改软链接指向。第三步启动前做一次状态检查启动前先确认redis-server -v能正常输出版本号。再确认老版本数据还在redis-cli ping # 老实例仍在线 ls -hl /data/redis/dump.rdb这里我习惯先把新实例用一个独立端口测试启动一次比如 6380用旧配置启动观察日志 10 秒确认没有加载错误后再进行真正的切换。这一步能拦截绝大多数配置兼容性问题。第四步正式切换服务如果你的 Redis 是 systemd 管着的改好软链接后直接重启服务systemctl restart redis redis-cli info server | grep redis_version如果新版本启动失败systemd 会返回失败状态此时先去日志里找原因不要急着反复重启journalctl -u redis -n 50 --no-pager3.3 systemd 场景下的特别注意点很多发行版自带的 Redis systemd 单元文件里会写死ExecStart和PIDFile。比如ExecStart/usr/local/bin/redis-server /etc/redis/redis.conf升级后软链接指向新版没问题但如果你的系统里/usr/local/bin/redis-server是旧版实文件且新二进制单独放在某个路径服线务启动的其实还是老版本。肉眼不一定能立刻发现只能看到redis_version没变。所以升级完记得再执行一次重启然后立刻看版本号。如果之前是用 SysV 方式/etc/init.d/redis管理的升级前最好提前切换到 systemd不要在生产环境里冒这个险。我遇到过更隐蔽的情况redis.conf里用daemonize yes并发起了 systemd 托管。结果 systemd 以为服务卡在启动阶段不断标记超时最后直接杀掉进程。新版 Redis 在 systemd 环境下应保持daemonize no让 systemd 直接接管进程生命周期配合Typeforking或Typesimple按实际配置选择。这里贴一个我在生产用的单元文件片段[Service] Typesimple ExecStart/usr/local/redis/redis-7.2.5/src/redis-server /etc/redis/redis.conf --daemonize no ExecStop/usr/local/redis/redis-7.2.5/src/redis-cli -p 6379 shutdown nosave Restartalways Userredis Groupredis注意shutdown nosave参数意思是停止前不强制保存快照避免停机时多一次 RDB 重写。如果你期望停机时保留最新数据那直接用shutdown save或干脆执行redis-cli shutdown。这个细节在自动化发布脚本里容易踩坑。3.4 主从架构的升级顺序别在主库上先动手主从架构下我最推荐的顺序是先在从库上升级确保从库能同步主库数据、能承载只读流量将从库数据一致性检查完成后利用replicaof切换或 Sentinel 触发主从切换原主库降级为新主库的从库再按同样步骤升级原主库这里面最关键的一步是升完从库后检查复制偏移量redis-cli -p 6380 info replication | grep master_link_status echo master_repl_offset: $(redis-cli -p 6380 info replication | grep master_repl_offset) echo master offset: $(redis-cli -p 6379 info replication | grep master_repl_offset)两个偏移量一致且master_link_status:up才说明数据同步正常。如果偏移量长期不一致先排查网络、持久化策略别急着切换。Redis Cluster 集群的滚动升级思路类似但还多一个要求升级完一个节点后必须等该节点的槽位数据同步正常再升级下一个节点。集群中有一个节点的版本和大多数不一致时CLUSTER INFO会显示cluster_state:ok但个别命令行为可能有差异。安全起见我都是按 master→slave 顺序逐对升级每对升级完跑一遍完整的读写探测再继续。4. 升级过程中的翻车现场与排查方法这部分我攒了不少真实案例都是我和同行在生产环境里踩过的。每一类我都尽量把现象、原因、对策写全。4.1 新版起不来报错集中在哪几类Bad directive or wrong number of arguments配置文件里有老版本支持的参数新版删掉了或语法变了。最常见的是save多参数写法和requirepass位置问题。对策把报错行注释掉一条条试但更推荐用新版本默认配置做底把你要覆盖的项手工填进去。Cant open the append-only fileAOF 文件路径不对或权限不对。升级后如果改了dir记得把 AOF 文件也拷贝过去或者配置保持原路径。Cant load the RDB fileRDB 文件是旧版本格式而新版本在加载时选择了不向后兼容。这种多见于大版本跨度较大的情况比如 2.x 直升 7.x。对策是多跨几步先升级到中间版本等数据加载完成后在中间版本上再做一次BGSAVE生成能被新版识别的 RDB。jemalloc: Unrecognized value编译时指定了错误的 jemalloc 配置比如MALLOCjemalloc但实际系统没有安装 jemalloc-devel。对策要么装上开发包后重新编译要么退回默认MALLOClibc生产环境建议优先补齐 jemalloc。no such file or directorysystemd 单元文件里ExecStart指向的二进制路径不对或符号链接没生效。我在多机发布时遇到过分发脚本漏掉软链接导致只有部分节点升级成功。4.2 升级后内存暴涨、碎片率下不来常见原因是新版本默认内存分配策略变了尤其是 7.x 对maxmemory-policy的语义调整。如果老配置里maxmemory设置得很保守而升级后业务访问量不变内存占用可能上涨 10%~20%。遇到这种情况不要慌分两步看第一步确认used_memory_human和used_memory_rss_human的差值。如果 RSS 远大于 used_memory说明碎片率偏高。执行memory purge或重启后再观察。如果重启后仍偶发考虑调整 jemalloc 的background_thread相关配置。第二步检查是否因为大量 key 过期策略变化导致惰性删除和定时删除的节奏不同。7.x 在active_expire上做了很多优化对服务器 CPU 是友好的但旧版本调的hz参数如果设置得太高升级后可能让 CPU 毛刺更明显。实验性地把hz调到 10 再观察。4.3 主从复制中断升级后大量断连我遇到过一次比较头疼的从库升级后master_link_status反复在up和down之间横跳。排查后是如下原因叠加主库client-output-buffer-limit replica设置得太低从库升级后同步大 key 或者全量同步时直接把输出缓冲区打爆网络带宽不够同步积压导致超时新版本默认repl-timeout比老版本短导致复制判断超时的阈值变敏感对策比较简单先检查主从间的master_repl_offset是否长期变化正常再看主库info clients里有没有连接被拒绝的日志。必要时可以临时调大repl-timeout同时在从库逐步接入业务读流量观察缓冲区的压力。还有一个很隐蔽的点从库升级前如果要保留旧数据建议打开replica-serve-stale-data yes旧版本叫slave-serve-stale-data。否则升级后主从第一次同步时从库会拒绝所有请求业务直接失败。这个问题在从库承载读流量时特别危险务必在配置里先设置好。4.4 回滚如果不彻底数据会二次受损回滚不是把软链接切回去重启就完事。新版 Redis 在运行期间可能已经改写了 RDB/AOF 文件格式直接切回老版本老版本可能不认识新版生成的文件。所以升级前备份的 RDB/AOF 文件回滚时优先用备份文件恢复如果回滚前新版已经运行较长时间备份文件比新文件更“老”用备份文件恢复会丢失升级期间的写入这个和回滚目标匹配正常业务发布周期可接受回滚完成后要让旧版本先加载一次 RDB 验证数据完整性再接入流量我在回滚演练中总结了一条保留升级前原目录不动升级时用全新目录回滚时不仅改软链接还要把数据文件恢复到升级前位置。这样才能保证旧版二进制和自己“熟悉”的数据文件配合避免格式问题。5. 升级后的验证与收尾怎么知道这次升级真成功了服务启动、版本号变了不代表升级成功。我有一张自检清单升级后逐项对照省得后面再返工。5.1 功能与数据完整性检查# 写读验证 redis-cli set upgrade:probe:$(date %s) value_ok ex 300 redis-cli get upgrade:probe:* # key 数量对比升级前记录的 keyspace redis-cli info keyspace # 持久化状态 redis-cli info persistence | grep rdb_last_bgsave_status redis-cli info persistence | grep aof_last_bgrewrite_status这里要注意如果升级前 old keyspace 有上万 key而升级后keyspace显示为 0先别慌很可能是你配置里maxmemory策略或者save策略没对导致 AOF/RDB 没加载。我建议升级前把dbsize打印到发布日志里升级后对比差值。如果 diff 在正常波动范围内比如 5% 以内说明数据基本完整。5.2 性能与长稳观察升级后的头 1 小时、头 24 小时是两个关键窗口。我会至少观察这几个指标latency用redis-cli --latency持续压测 1000 次观察平均值是否有明显劣化CPU 使用率top里 redis 进程 CPU 是否持续飙高尤其是开启 io-threads 后会不会出现某个 CPU 核打满内存碎片率info memory里mem_fragmentation_ratio长期在 1.5 以上要警惕慢日志slowlog get 50看是否有异常慢命令7.x 引入的debug leak和info keyspace数据统计能让定位问题更快但前提是你对新版的监控指标足够熟悉。我建议升级后先跑一轮redis-benchmark做一个简单的压力测试对比升级前后的 QPS 基线而不是直接把线上流量放过去。redis-benchmark -c 50 -n 100000 -d 128 -t set,get -P 16如果压测结果比升级前低 5% 以上优先检查配置里save策略、appendfsync频率、maxmemory是否带来额外负担。有时候是appendonly yes从关到开导致的性能下降这属于预期内的成本不一定是升级本身的错。5.3 清理与文档这些杂事别人看不到但很有用升级完成后删除临时 RDB 备份目录里的中间文件避免占用磁盘更新 systemd 单元文件里的版本注释在运维文档或公司内部知识库里留下升级记录升级时间、原版本、目标版本、配置差异、验证结果、回滚预案把升级前的redis.conf打一个带时间戳的副本放到独立的备份目录里方便以后 diff我常年在服务器目录里放一个UPGRADE.md每次升级前先把当前版本和配置 diff 记录下来升级后再把结果写进去。半年后回看效率提升非常明显。这也算是我给新人的一个“偷懒”建议。5.4 万一还有问题要不要再回滚如果升级后一切正常那自然不需要回滚。但如果出现运行时崩溃、数据持续不一致、性能严重劣化且排查半天无解那就执行回滚而不是硬扛。回滚的正确姿势是# 停掉新版服务 systemctl stop redis # 切回旧版软链接 ln -sfn /usr/local/redis/redis-6.2.x/src/redis-server /usr/local/bin/redis-server # 用升级前备份的数据启动旧版 systemctl start redis注意如果新版运行期间已经发生过写操作且备份文件是升级前生成的回滚后必然丢失这段时间的数据。这个小窗口损失需要和业务方确认。所以我不建议在任何高写入场景下做“先升级靠回滚兜底”的冒险升级前必须有清晰的数据备份。6. 过程中那些“网上不会教你”的细节最后把一些散点经验聚在一起方便你直接收藏至少能省半天排查时间。6.1 别把 Redis 编译放太低配的服务器上Redis 的make阶段其实不吃资源但make test如果跑完整测试会比较吃 CPU。如果服务器规格只有 1C2G编译时建议直接跳过make test用make -j2限制并行度避免编译期间 CPU 飙高影响线上业务。6.2 升级后立刻调整hz与动态参数新版 Redis 支持不少动态可调参数不用改配置就能在线调整。升级后我习惯顺手把latency-monitor-threshold打开redis-cli config set latency-monitor-threshold 100这样就能在latency命令里看到微秒级延迟事件比事后看慢日志快得多。更妙的是这个参数可以随时关掉不用重启。6.3 如果业务里有自定义模块升级前先确认模块热度Redis Modules 的 API 在每个大版本都会有小变动。比如 RedisTimeSeries 在 6.x 和 7.x 下的兼容性就有差异老版本模块直接加载到 7.x 里可能出现Module API mismatch。升级前一定要把模块先升级到与目标 Redis 版本匹配的最新版并且加载顺序要放在数据加载之后、对外提供服务之前。6.4 定时任务与发布窗口错峰我见过有团队在白天高峰期升级 Redis主从切换顿时让慢查询数量翻了三倍。虽然 Redis 本身快但主从切换时客户端重连带来的并发抖动是必然的。所以升级窗口最好选在凌晨低峰期且在任何“大促”“压测”“报表日”之前完成验证。6.5 写脚本要小心的一个坑redis-cli版本必须与redis-server匹配升级后如果只替换了redis-server、忘了替换redis-cli老版本的redis-cli连接新版 Redis 时可能因为 RESP3 或新命令而解析错乱。轻则info里缺字段重则某些命令直接报语法错误。所以我软链接里永远同时替换这两个二进制这一点最容易被忽略。最后说说我的体会我最早折腾 Redis 升级是因为线上 4.0 实例的 AOF 重写经常把主进程阻塞到几十毫秒业务侧一直报超时。那时我花了两个晚上做备份、搭临时从库、编译 6.2、灰度切换最后整个过程平稳得超乎我预期。反而是后来第二次升级时因为没做配置 diff新版直接把save策略解释错了差点把数据目录写爆。所以我现在给人讲 Redis 升级每次都强调一句话升级的核心难点从来不是新版好不好用而是你有没有把旧版本的环境、配置、数据摸透。把勘察和备份做到位切换随随便便就能完成这两个环节偷懒后面一定会用报警把你叫醒。如果你也准备给自己服务器上的 Redis 动刀建议先拿这套流程在测试环境完整走一遍记录时间节点、备份大小、验证结果。确认万无一失后再去生产环境操作。另外我强烈建议你升级完成后顺手跑一遍redis-cli config rewrite把动态调整的参数固化进配置文件里免得下次重启时参数又丢了。升级这事稳妥比速度重要。希望这篇能让你少踩几个坑少熬两个夜。