案例 Docker 里把 Redis 6.2 升到 8.8:dbsize 变 0、数据像丢了、启动直接 FATAL

2026-09-29 21:03:00

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 等新类型与命令,老版本从节点认不出,按「先升从、再切主、最后升老主」的顺序滚动升级。
复制全文 生成海报 Redis Docker 运维 持久化 升级 AOF

推荐文章

程序员茄子在线接单