Docker 里把 Redis 6.2 升到 8.8:dbsize 变 0、数据像丢了、启动直接 FATAL
项目信息
- Redis 官方 releases(含 8.8 release notes):https://github.com/redis/redis/releases
环境与前情
生产环境的 Redis 跑在 Docker 里,这次要把镜像从 redis:6.2-alpine 换到 redis:8.8-alpine。实际遇到两种部署方式,坑不一样:
- 数据挂在匿名 volume 上:
docker run时没有指定-v。 - 数据挂在宿主机目录上,并且容器里带一份自定义
redis.conf。
场景一:匿名 volume(最常见也最危险)
匿名 volume 的名字是一串随机哈希。直接删容器重建,新容器会挂上一个全新的空 volume,旧数据不会跟过去,dbsize 看起来就像数据丢了。
正确做法:停容器前先 BGSAVE 确保落盘,然后把数据 docker cp 出来,再改成 bind mount 挂到宿主机目录,顺便把挂载方式规范化。
# 停容器前先落盘
docker exec redis-cli -a '' BGSAVE
# 把容器内的数据目录 cp 出来
docker cp :/data ./redis-data
场景二:宿主机目录挂载 + 自定义配置
数据本来就在宿主机上,不需要迁移,重点变成配置文件兼容性检查。6.2 时代的一部分配置项在 8.x 已经被移除,比如 list-max-ziplist-* 系列旧名。配置文件里还留着这些行,8.8 启动会直接失败:
FATAL CONFIG FILE ERROR
另外宿主机挂载目录要注意属主,chown 999:999 ,让权限和镜像内 redis 用户的 uid 对齐,否则容器内写不进去。
坑 1:匿名 volume 的数据不会跟着容器走
docker run 不指定 -v 时会生成匿名 volume,删容器重建后,新容器挂的是另一个空 volume。数据迁移必须先把数据 docker cp 出来,再以 bind mount 方式挂回去。
坑 2:多行命令粘贴被截断
带反斜杠换行的 docker run 命令粘到终端执行,参数可能被截断。比如 --appendonly yes 变成 --appendonly ye,容器起不来,陷入重启循环。
坑 3:开了 AOF 之后不加载 dump.rdb,dbsize 返回 0
这是本次最大的坑。重建容器时给启动参数加了 --appendonly yes,启动后 dbsize 返回 0。
原因:开启 AOF 后 Redis 只从 AOF 文件恢复,不再读 dump.rdb;启动时发现没有 AOF 文件,就创建了一个空 AOF,数据集是空的,RDB 里那份数据等于没被加载。
存量数据在 RDB、又想要 AOF 的话,分两步走:
# 1. 先以纯 RDB 模式启动(不带 --appendonly yes),让数据加载回来
# 2. 确认数据恢复后,动态开启 AOF,Redis 会基于当前数据集生成 AOF 文件
docker exec redis-cli -a '' CONFIG SET appendonly yes
回滚
换回旧镜像 tag 重建容器即可,volume / 挂载不变。注意 RDB 是单向兼容的:8.x 运行过之后新写的 RDB,6.2 读不了;回滚时需要用冷备份的旧 dump.rdb 覆盖回去再启动老镜像。
其他要点
- 升级前先
docker inspect搞清楚数据在哪:匿名 volume、命名 volume,还是 bind mount。 - 匿名 volume 必须先迁移数据到宿主机目录,顺便把挂载规范化。
- 用了自定义
redis.conf的,先检查配置项与新版本的兼容性。 - 版本跨度大(6.2 → 8.8),协议层 RESP2 完全兼容,客户端一般不需要改代码;但 Lua 脚本缓存会清空,标准客户端(Redisson / Spring Data Redis)会自动重载。
- Redis 8.8 新增 Array / INCREX / XNACK 等新类型与命令,老版本从节点认不出,按「先升从、再切主、最后升老主」的顺序滚动升级。