ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Redis版本升级全流程:兼容性检查、滚动迁移与踩坑复盘

Redis版本升级全流程:兼容性检查、滚动迁移与踩坑复盘 前阵子我把一组线上 Redis 实例从 6.2 升到了 7.2前后折腾了大半天踩了配置迁移、ACL 权限和客户端超时三个坑。这次升级给我的教训是升级 Redis 这件事本身不难难的是升级前把兼容性和回滚方案想清楚、升级时不让业务闪断、升级后能快速定位异常。这篇就按这个顺序写把完整流程、命令和踩坑记录都放出来以后你遇到“Redis 版本太旧想升到最新”照着走就行。1. 升级前先把版本这事想透1.1 Redis 版本号怎么读稳定版是哪条线Redis 版本号分三段主版本、次版本、补丁版本。比如 6.2.14主版本是 6次版本是 2补丁是 14。这里有个规律很多人不知道次版本为偶数的都是稳定版比如 6.0、6.2、7.0、7.2次版本为奇数的属于开发版/不稳定版线上生产环境不要碰。所谓“升级到最新版本”其实有两层含义一层是官方当前最新版比如 7.2.x 或 8.0.x另一层是你当前版本线下的最新补丁比如你还在 6.2那就先升到 6.2 的最后一个补丁。我的习惯是生产环境优先升到当前稳定线的最新补丁大版本升级滞后半个月到一个月等社区反馈稳定了再动。你问我什么叫“最新版本”我的答案很实际经过验证、能在你的环境里跑稳的那个版本而不是 Release 页面上数字最大的那个。1.2 什么样的情况值得升级什么样的情况别硬升升级 Redis 不是换个二进制那么简单它动的是一套正在提供服务的缓存/存储系统。值不值得升看下面几个驱动因素值得升级的情况旧版本存在安全漏洞特别是未授权访问、ACL 绕过这类问题新版本修复了你正在踩的 bug比如特定数据结构下的崩溃、主从同步异常需要新功能比如 Redis 7 引入的 Redis Functions、改进的集群迁移机制、多部分 AOF官方宣布旧版本停止维护不再发布补丁不该硬升的情况项目里用了第三方 Redis 模块模块还没适配新版本客户端库太老且没有更新计划连 RESP3 协议都不认识当前正值大促、业务高峰窗口期内没法接受任何异常没有可用的完整备份也没准备回滚方案线上 Redis 的数据格式有严重的历史包袱比如还在用旧版 RDB 且升级路径未验证1.3 升级的本质风险到底是什么很多人以为升级 Redis 的风险在“数据会丢”其实按我的经验数据丢失反而是最小概率的事。真正的风险有三类第一配置兼容风险。新版启动时会严格要求配置格式旧配置里的废弃指令可能让进程直接起不来。第二客户端兼容风险。Redis 大版本升级往往伴随协议、命令语义上的调整客户端库版本不跟上的话轻则命令报错重则超时、断连。第三持久化格式风险。新版能读旧版的 RDB/AOF但一旦新版把数据文件重写成新格式旧版就回不去了回滚路径就断了。把这三个风险控制住升级就算成功了一大半。下面这几节的准备工作全部围绕这三个风险展开。2. 升级前三个小时的必做功课2.1 先把自己家底摸清楚别一上来就敲升级命令。先花十分钟把线上环境的基本信息收集全。我每次都执行这几条redis-server --version redis-cli -p 6379 INFO server | grep -E redis_version|os|process_id|run_id redis-cli INFO persistence | grep -E rdb|aof|loading redis-cli INFO keyspace redis-cli INFO memory | grep -E used_memory_human|maxmemory_human|mem_fragmentation_ratio这里重点是搞清楚三件事当前版本号、是否启用了 RDB/AOF、总共有多少逻辑库和 key 量级。版本号决定你的升级跨度持久化配置决定备份怎么做数据量级决定你评估加载耗时和内存变化。还要搞清楚部署方式是用 apt/yum 装的、源码编译的、Docker 容器还是裸机二进制配置文件在哪个路径服务由 systemd 管理还是 init.d主从拓扑怎么编排是不是有 Sentinel 或 Cluster 在管如果连这些都不确定请先查清楚再动手。2.2 对照 CHANGELOG 排查不兼容点每条稳定的升级路径官方都会在 Release Notes 里写清楚破坏性变更。我强烈建议把目标版本和当前版本之间的所有 Release Notes 通读一遍重点盯几类内容配置指令的废弃或改名。Redis 历史上干过这种事比如某些参数在新版本不再生效且启动时直接报错ACL 相关变更。Redis 6 引入 ACLRedis 7 又强化了 ACL 规则旧配置里的 requirepass、user 指令语义可能发生变化命令行为变化。比如某个命令在旧版本返回 A新版本返回 B这会影响业务代码Lua 脚本复制方式的变化。Redis 7 对脚本效果复制有调整会影响分布式锁、限流这类涉及脚本的场景RDB/AOF 文件格式版本。Redis 新版通常可以读旧格式但反过来不行所以一旦启动新版旧文件就会被改写这里给个方法论不要只看别人写的升级总结直接翻官方 GitHub 的 release notes英文看不懂就翻译着看哪怕花一个小时也比线上翻车强。2.3 备份和回滚方案必须在动手前定下来升级前一定做一次完整备份操作很简单redis-cli BGSAVEBGSAVE 会后台生成一份 RDB 快照然后确认备份文件时间戳确实更新了把它复制到另一台机器或磁盘ls -l /var/lib/redis/dump.rdb cp /var/lib/redis/dump.rdb /backup/redis-upgrade-backup/dump-6.2-$(date %F).rdb如果线上是 AOF 模式顺手把 AOF 文件也复制一份。备份的目的是回滚时能恢复到升级前的数据状态这里有个很容易被忽略的点备份完之后不要让业务继续大量写入否则这份备份就代表不了升级瞬间的数据。最好的做法是备份之后尽快操作升级中间间隔越短回滚越精准。同时把旧版本的可执行文件保留好。源码编译安装的旧二进制不要删Docker 部署的旧镜像不要删包管理器安装的记住当前版本号。这样如果新版有问题可以用旧版本直接启动服务。2.4 一个很容易被忽略的准备先在一台测试机跑一遍这个习惯救过我无数次。找一台和线上配置接近的机器用备份的 RDB 启动新版 Redis观察启动日志、加载耗时、内存占用、配置解析是否报错。测试机跑通了线上才动手。你可能会问“没测试机怎么办”那就用 Docker 起一个临时容器把 Redis 数据目录挂进去做验证成本很低。3. 四种主流升级路径按你的环境对号入座3.1 Debian/Ubuntu 下用包管理器升级如果你是 apt 安装的 Redis最简单的方式是sudo apt update sudo apt-cache policy redis-server sudo apt install redis-serverapt 会帮你把 Redis 升到软件源里可用的最新版本然后重启服务sudo systemctl restart redis-server systemctl status redis-serverCentOS/RHEL 则是yum update redis systemctl restart redis这里必须提醒很多发行版仓库里的 Redis 版本滞后严重。Ubuntu 默认源里的 redis-server 可能停留在 6.0 甚至更早你 apt install 半天还是旧版。想用官方最新版要么配置 Redis 官方 apt 源在 download.redis.io 上有说明要么直接走源码编译我更推荐后者可控性更高。3.2 源码编译升级最通用也最可控的方式源码编译是目前最稳妥的升级方式之一我来写下完整的操作序列以 7.2.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 make -j$(nproc) make test sudo make installmake install 默认会把二进制装到 /usr/local/bin覆盖掉旧的 redis-server。如果你不想覆盖旧版本就用 PREFIX 指定新目录make PREFIX/usr/local/redis-7.2 install这样新版本装进独立目录旧二进制原封不动回滚只需要改一下 systemd 的启动路径。用这种方式升级还牵扯到一个细节systemd 服务文件里的 ExecStart 指向哪个 redis-server很多人的服务文件写的是绝对路径比如 /usr/local/bin/redis-server那 make install 之后其实就已经指向新版本了重启服务即可。但如果装到了自定义目录就要手动改 service 文件改完记得sudo systemctl daemon-reload sudo systemctl restart redis-server3.3 Docker 容器升级注意别把数据搞丢Docker 升级 Redis 最大的坑不是命令而是卷。很多人在 docker run 的时候没指定命名卷容器删了就什么都没了。升级前先看清楚docker ps --format table {{.Names}}\t{{.Image}}\t{{.Ports}} docker inspect 容器名 | grep -A5 Mounts确认数据目录挂载到哪一般是容器内的 /data 对应宿主机的一个目录或命名卷。然后拉新镜像docker pull redis:7.2-alpine接下来两种做法我的建议是优先用 docker composedocker compose pull docker compose up -d如果原来是裸 docker run 启动的就手动重建。把旧容器重命名保留防止回滚时需要docker rename 旧容器名 旧容器名-old docker run -d \ --name 新容器名 \ -v /宿主机数据目录:/data \ -p 6379:6379 \ redis:7.2-alpine \ redis-server /usr/local/etc/redis/redis.conf注意新容器要复用旧容器挂载的同一个数据目录这样才能读到原有 RDB/AOF。启动后马上看日志确认数据加载成功。国内网络拉镜像可能不稳定如果 docker pull 老失败就配置一下国内镜像加速器或者直接 pull 镜像仓库里的镜像。具体地址各家不一样这里不展开。3.4 Windows 环境的升级方案Redis 官方并不发布 Windows 原生版本现在 Windows 上用的一般是开源社区的移植版比如 tporadowski/redis或者 Memurai 这类商业兼容方案。升级流程大概是停掉旧服务redis-server --service-stop卸载旧服务redis-server --service-uninstall先解绑服务名把整个旧 Redis 目录复制一份做备份包括 redis.windows.conf 和数据文件下载新版 Windows 版 Redis 包解压到新目录把旧配置文件拷进新目录检查过时指令后另存为 redis.windows-service.conf安装并启动服务redis-server --service-install redis.windows-service.conf --service-name RedisNew启动redis-server --service-start然后用 redis-cli 验证Windows 下最容易踩的坑是服务名冲突和服务路径残留。很多人升级失败是因为旧服务还在占着 6379 端口或者卸载不干净。所以我步骤里特意把 uninstall 放在 install 前面这个顺序别颠倒。4. 高可用架构下怎么做到业务不中断4.1 主从架构的滚动升级思路如果你的 Redis 是主从结构千万别直接在主节点上重启那样会有几秒钟的写入中断。标准做法是“从节点先行逐台替换”先把所有从节点的 Redis 升到新版本重启并验证同步正常挑一个数据最全的从节点执行REPLICAOF NO ONE把它切换成新主节点修改业务客户端配置把连接切到新主节点或者如果前面有 SentinelSentinel 会自动完成切换把旧主节点也升到新版本然后执行REPLICAOF 新主节点IP 新主节点端口把它变成新主的从节点这种滚动方式把停机时间压缩到切换那几秒钟如果你配合 Sentinel 自动故障转移业务侧甚至无感知。4.2 Redis Cluster 集群的滚动升级顺序Cluster 环境升级要格外谨慎因为故障转移有仲裁机制同时挂掉太多节点会导致整个集群不可用。推荐顺序看当前集群状态确认没有迁移任务在进行redis-cli --cluster info 任意节点IP:端口逐个节点操作先升级从节点再通过CLUSTER FAILOVER把主节点切走每次只动一个节点处理完确认集群恢复 OK 状态再动下一个升级过程中不要同时重启多个主节点否则可能出现部分分片失去主节点触发集群不可用单节点上的升级操作和普通 Redis 一样关键是节奏。一边动一边观察别图快。Cluster 的滚动升级顺序我通常遵循所有从节点 → 手动触发第一个 failover → 原主节点重启升级后变回从节点 → 再处理下一个分片。4.3 Sentinel 哨兵模式的升级细节Sentinel 本身对 Redis 版本不敏感但为了稳妥我的建议是先升级哨兵节点再滚动升级数据节点升级所有 Sentinel 进程重启后确认能正常发现主从节点对数据节点执行第 4.1 节的滚动升级全程观察 Sentinel 日志确认没有误判故障触发 failover这里有个容易被忽略的点Sentinel 配置文件里有sentinel monitor master-name ip port quorum升级后如果主节点 IP 或端口变了要同步更新哨兵配置否则哨兵盯的还是旧地址。5. 升级后的验证清单以及那个最磨人的超时问题5.1 升级后的第一轮验证五分钟内做完服务启动后第一时间执行这些检查redis-cli --ping redis-cli INFO server | grep redis_version redis-cli INFO keyspace redis-cli INFO memory | grep -E used_memory_human|maxmemory_human|mem_fragmentation_ratio redis-cli INFO stats | grep -E total_connections_received|instantaneous_ops_per_sec核对几件事ping 返回 PONG版本号确实变成新版key 数量和升级前对得上内存和碎片率在合理范围连接数恢复到了正常水平。同时把 Redis 自身日志拉出来看一眼有没有Bad directive、Cant handle RDB format version、AOF rewrite这类告警。5.2 功能验证重点测缓存、分布式锁和序列化技术验证之外业务功能也要实测。最值得测的是三个场景第一缓存读写。用业务里常跑的接口打一轮请求确认 get/set/expire 都正常。第二分布式锁。如果你用了 SETNX EXPIRE 或者 Redisson 这类锁工具一定要重点验证加锁、解锁、超时续期这些流程因为 Redis 7 对脚本语义有过调整脚本类实现最容易受影响。第三序列化。用了 GenericJackson2JsonRedisSerializer 这类序列化器的确认缓存 key/value 在新版本下读写一致别出现反序列化失败。5.3 Lettuce 客户端超时升级后最经典的故障现场升级过程中和升级后最常见的异常就是超时。典型报错长这样Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException先说结论升级本身带来的短暂超时往往是正常的因为服务重启导致连接断开老连接会被服务端清理客户端重新建连的几秒内请求会感知到延迟。但如果升级完成一段时间后仍持续超时就得认真排查了。我见过的情况里最常见的原因是连接池配置不合理。Spring Boot 默认用 Lettuce默认连接池 max-active 只有 8并发一高就很容易打满新请求排队等待连接最后触发超时。这种问题在升级前可能被低流量掩盖升级后业务流量一恢复就暴露了。调整方式spring: redis: timeout: 3000ms lettuce: pool: max-active: 50 max-idle: 20 min-idle: 5 max-wait: 3000ms还有一个排查方向客户端库版本太老。Redis 大版本升级后建议把 spring-data-redis、lettuce-core 的版本也同步升一升特别是从 Redis 5 直接跳 7 的场景老客户端对 RESP3、ACL 命令的兼容性都有限。5.4 可视化客户端和监控告警的确认升级完成后用 Redis Insight 或 Another Redis Desktop Manager 连接一下确认可视化工具能看到键空间、能执行常用命令。这里有个小坑有些旧版本可视化客户端对新版本的命令过滤得不准确比如看不到某些新类型 key 的详情建议顺手把客户端也升到最新版。同时确认监控告警正常慢日志、连接数、内存使用率、主从同步延迟这些指标有条件的把 Prometheus redis_exporter 的采集目标跟着新地址/新端口更新一遍。6. 升级踩坑实录这几个问题最容易忽略6.1 配置文件里藏着的过时指令有一次升级我自信满满地重启服务结果 Redis 直接退出日志里写着解析配置文件出错。原因是旧配置里用了一个在新版本中已经废弃的指令新版不认识就直接报错而不是忽略它。解决方式是升级前先在测试机上用新版本二进制解析一遍旧配置/usr/local/redis-7.2/bin/redis-server /etc/redis/redis.conf如果启动失败日志会告诉你哪一行配置有问题改完再试。别到了线上才发现那会儿就尴尬了。6.2 ACL 权限和默认用户跨大版本最容易翻车的地方Redis 6 引入 ACL 之后默认用户和 requirepass 的语义有变化。如果你从 5.x 直接跨到 7.x而旧配置里既有 requirepass 又有自定义 user 指令新版对密码和 ACL 规则的校验更严格配置解析不过去是小事更麻烦的是启动后连接鉴权失效业务全线报NOAUTH。我的经验是跨大版本升级前专门把 ACL 相关的配置梳理一遍CONFIG GET requirepass、CONFIG GET user把当前生效的权限规则导出来升级后逐条验一遍。尤其注意protected-mode和bind的配合别把没设密码的 Redis 裸奔到公网。6.3 升级后内存占用变高缓存治理反而要回炉重审Redis 7 在大 key 淘汰、内存编码上做过优化但有时候升级后你会看到used_memory比升级前还高。这不一定是你操作失误而是新版本的数据结构编码、内存分配策略有变化。处理办法分两步。第一步用MEMORY DOCTOR和MEMORY PURGE看内存碎片情况必要时开启activedefrag yes。第二步提醒你升级不是缓存治理的终点。之前设置的 maxmemory-policy比如 volatile-lru、allkeys-lfu在数据规模变化后是否还合适bigkey 是否还占着大头过期扫描是否拖累性能这些要借着升级的契机重新审视一遍。缓存穿透、击穿、雪崩这些治理手段在新版本下同样要做压力验证别以为版本新了就万事大吉。6.4 数据加载变慢大页文件把启动时间拉上天从旧版本升级后第一次启动需要加载 RDB 文件。如果数据量大加载耗时会比较明显。Redis 7 支持异步加载部分过滤掉闲时加载但我的建议是升级窗口预留足够的启动时间一般给到正常加载耗时的 2 倍。如果加载到一半被 killRDB 没加载完是不会正常提供服务的。这个问题在升级排障里被很多人忽略所以我放进踩坑实录里。实际遇到的时候别慌看日志Loading dataset等它跑完就行。6.5 回滚操作比你想象中更容易翻车升级后如果发现问题要回滚最直接的方式是停掉新版本用旧二进制配旧配置文件重新启动systemctl stop redis-server /usr/local/bin/redis-server-6.2 /etc/redis-backup/redis.conf但是要注意如果新版启动时已经写过 RDB 或 AOF文件格式很可能已经变成新版格式旧二进制读不了。所以回滚的正确前提是回滚时先把升级前备份的 RDB 文件还原回去再用旧二进制启动。这就是为什么我在第 2.3 节反复强调备份的重要性。没有一份干净的、记录当时数据的备份回滚就是一句空话。最后一点个人体会升级 Redis 这件事操作层面半小时到一小时就能做完真正花时间的是准备工作。我后来每次升级都会强制自己走一遍完整流程摸清现状、通读变更、备份留底、测试机预演、滚动升级、验证清单、回滚预案。这套流程看起来繁琐但每一次都让事故率低了不少。最后分享一个我在实际操作中养成的习惯升级后旧版本二进制和备份文件至少保留三天。三天内没问题再清理三天内一旦有问题随时能退回去。如果你正准备升一个大版本我的建议是先在 staging 环境把 ACL、配置解析、Lettuce 超时参数这些全部过一遍再碰线上。这样虽然慢一点但是晚上能睡个踏实觉。
返回列表