PostgreSQL 换机器后数据全丢:Docker 数据卷 uid 错位为何比 down -v 更隐蔽
本文摘要换机迁移后数据卷 uid 错位旧库被判为不存在而静默重建。入口脚本以 PG_VERSION 可读性判断初始化权限阻断即误判。文件级迁移仅适合同版本大库跨版本应改走 pg_upgrade。一、问题与结论一套docker compose部署的 PostgreSQL 16 从旧机器搬到新机器docker compose up -d一切正常业务查询立刻报错ERROR: relation orders does not exist容器日志只有database system is ready to accept connections没有删除记录、没有告警。原因是 named volume 里的文件按数值 uid 落在宿主机上迁移后属主漂移成0:0新机器的 compose 多了一行user: 1000:1000官方镜像 entrypoint 的 root 分支被跳过$PGDATA/PG_VERSION对 uid 1000 不可读[ ! -s $PGDATA/PG_VERSION ]判定为空于是静默initdb建了一个空库。这比docker compose down -v更隐蔽down -v是显式命令下一次up就会发现数据没了uid 错位触发的重建没有任何提示新库照常接客等业务查不到表时可能已经在空库上写了半天新数据。二、排查与选择依据排查三步先看数据卷属主数值再看容器启动身份最后用docker compose logs postgres | grep -iE initdb|initdb: warning找第二次初始化的痕迹。dockervolume inspect pgdata--format{{ .Mountpoint }}sudols-ln/var/lib/docker/volumes/pgdata/_data|head-3dockerrun--rm-vpgdata:/data alpinels-ln/data/PG_VERSION /data/base判据两条base及其下文件的属主数值应与容器内postgres一致docker run --rm postgres:16-alpine id postgres可查官方镜像通常为uid999(gid999)PG_VERSION必须能被启动身份读到注意它的模式常为0600。dockercompose config|grep-A2userdockercomposeexecpostgresid只要user:的 uid 与postgres不同、而数据属主又是第三个数值权限错位就不会被 entrypoint 修复。initdb.d 不会重复执行补救要靠重放/docker-entrypoint-initdb.d只在数据目录初始化时执行一次。数据被重建后把脚本重新挂进去也不会再跑而为了让它重跑去清空PGDATA等于主动销毁数据。补救顺序从备份恢复数据目录 →chown -R 999:999修属主 → 手动psql -f重放 initdb.d 里的建表、扩展、账号脚本并逐条核对pg_extension、pg_roles、pg_hba.conf的手工改动。替代方案与取舍方案选择条件代价不该用的场景pg_dump/pg_restore中小库、可跨大版本、目标环境 uid 不可控导入耗时随数据量线性增长不保留物理布局上百 GB 且停机窗口以分钟计pg_upgrade同机跨大版本、停机窗口内完成需新旧 bin 目录并存扩展需逐个验证主从集群整体迁移、目标机版本不明pg_basebackup大库物理迁移、同版本要求源库在线并开放 replication 权限跨大版本、源库无法改pg_hba.conftar --numeric-owner同版本文件级搬运、追求最短窗口强依赖 uid 语义一致目标机启用userns-remap、自定义 entrypoint文件级拷贝只解决“搬运”不解决“版本”与“权限语义”只要目标环境 uid 语义或主版本不同就应换上面三种之一。三、关键原理官方镜像docker-entrypoint.sh的初始化判断是“文件是否存在且非空”而这个判断受读权限与父目录可遍历性影响if[!-s$PGDATA/PG_VERSION];thendocker_init_database_dirfiif[$(id-u)0];thenfind$PGDATA\!-userpostgres-execchownpostgres{}execgosu postgres$BASH_SOURCE$fi三层叠加才致命named volume 的文件以数值 uid/gid 落盘不绑定用户名tar/rsync迁移时若按用户名映射属主会在目标机漂移以非 root 启动时chown分支被跳过。最终PG_VERSION对启动身份不可读或不可判定为非空-s测试返回假entrypoint 把“读不到”当成“不存在”initdb静默重建。日志里看不到任何“数据丢失”字样。四、可运行示例环境Docker Engine 20.x 与 compose v2镜像postgres:16-alpinenamed volumepgdata。# docker-compose.ymlservices:postgres:image:postgres:16-alpineenvironment:POSTGRES_PASSWORD:demovolumes:-pgdata:/var/lib/postgresql/datavolumes:pgdata:dockercompose up-dsleep5dockercomposeexecpostgres psql-Upostgres-c\CREATE TABLE orders(id serial, amount int); INSERT INTO orders(amount) VALUES (100),(200);# 导出数据目录模拟迁移到新机器未加 --numeric-ownerdockerrun--rm-vpgdata:/data-v$PWD:/backup alpinetarczpf /backup/pgdata.tar.gz-C/data.dockercompose downdockervolumermpgdatadockervolume create pgdatadockerrun--rm-vpgdata:/data-v$PWD:/backup alpinetarxzf /backup/pgdata.tar.gz-C/data# 模拟迁移后的属主漂移dockerrun--rm-vpgdata:/data alpinechown-R0:0 /data复制docker-compose.yml为docker-compose-user.yml在services.postgres下加一行user: 1000:1000模拟新机器差异启动后验证dockercompose-fdocker-compose-user.yml up-dsleep5dockerrun--rm-vpgdata:/data alpinels-ln/data/PG_VERSIONdockercompose-fdocker-compose-user.ymlexecpostgres psql-Upostgres-cSELECT * FROM orders;预期输出ls -ln显示PG_VERSION属主999:999SELECT返回 2 行。实际输出属主为0:0SELECT报relation orders does not exist日志无任何异常。修复dockercompose downdockerrun--rm-vpgdata:/data alpinechown-R999:999 /datadockerrun--rm-vpgdata:/data alpinechmod700/datadockercompose up-d# 去掉 compose 中的 user: 字段以 root 启动让 entrypoint chown常见失败去掉user:后仍报FATAL: data directory /var/lib/postgresql/data has group or world access原因是迁移时目录权限被放宽到0755修复是chmod 700后再启动而不是放宽 PostgreSQL 的权限检查。五、验证结果与边界验证三条SELECT能查到迁移前的数据行ls -ln属主与id postgres一致容器日志中没有第二次initdb/initdb: warning痕迹。三条任一不满足都应视为数据未就位。单机场景停机窗口内二选一——同版本用文件级迁移或pg_basebackup跨大版本用pg_upgrade新旧 bin 目录并存扩展需逐个确认。主从场景不要把主库文件目录直接拷给从库从库一律pg_basebackup重建pg_upgrade只能对单节点离线执行升级完主库后重建 standby再恢复复制。边界条件目标机启用userns-remap时数值 uid 在宿主机侧含义改变--numeric-owner也不足以保证一致自定义 entrypoint 的镜像需自查判断逻辑上述复现在postgres:16-alpine上执行过13–17 主版本的判断逻辑表述一致但未在每个主版本逐一验证。参考资料docker-entrypoint.shdocker-library/postgres master 分支Docker Volumes 官方文档Docker Engine 20.x–27.xpg_upgrade — PostgreSQL 16 官方文档SQL Dump — PostgreSQL 16 官方文档pg_basebackup — PostgreSQL 16 官方文档

相关新闻

Func、Skill 与 MCP:Agent 工具调用三层架构实战指南

Func、Skill 与 MCP:Agent 工具调用三层架构实战指南

最近在折腾 Agent 开发时,被一个概念问题搞得有点上头:工具到底该用 func、skill 还是 MCP?搜索引擎的结果五花八门,有说 skill 是未来,有说 MCP 才是标准,还有的直接把 function calling 当成全部。等我把…

2026/10/10 17:40:35 阅读更多 →
Android HTML答题引擎:Kotlin+Java双语言深度集成方案

Android HTML答题引擎:Kotlin+Java双语言深度集成方案

简介:这是一份面向Android开发初学者与进阶学习者的HTML整合型答题APP完整源码项目,适用于移动应用开发实践、混合式界面设计及Kotlin/Java协同开发场景。资源共786个文件,总大小47.87MB,涵盖182个Java与8个Kotlin源文件&#xff…

2026/10/10 20:05:11 阅读更多 →
手写BP神经网络实现鸢尾花和红酒分类:从原理到避坑

手写BP神经网络实现鸢尾花和红酒分类:从原理到避坑

简介:这是一份面向高校机器学习课程的BP神经网络实验资源,以鸢尾花与红酒数据集为对象,完成二分类/多分类建模练习,适合正在学习前馈神经网络、反向传播算法或需要快速搭建课程实验的学生参考。压缩包共收录18个文件,包…

2026/10/10 20:49:15 阅读更多 →

最新新闻

哈工大SSE第33题:5×5矩阵鞍点C语言实现详解

哈工大SSE第33题:5×5矩阵鞍点C语言实现详解

如果你正在刷哈工大 SSE 这套 C 语言编程练习,到第 33 题附近,大概率会撞上一个老熟脸:求 55 矩阵的鞍点。这道题说难不难,说简单也不简单,很多刚学完二维数组的同学会卡在这一题上。作为一个给 SSE 写过代码、也给学弟…

2026/10/10 20:49:33 阅读更多 →
零基础 4 小时学会用 AI 干实际工作:learn-opencode(OpenCode 中文实战课)完整学习路线图

零基础 4 小时学会用 AI 干实际工作:learn-opencode(OpenCode 中文实战课)完整学习路线图

文档教程 【免费下载链接】learn-opencode OpenCode 中文实战课源码与内容仓库:一课一页,覆盖入门到实战工作流。 项目地址: https://gitcode.com/gh_mirrors/le/learn-opencode 点击查看 免费下载 learn-opencode 是一门完全免费、开源的 O…

2026/10/10 20:49:33 阅读更多 →
Wolfram语言编程思维:从表达式到函数式,告别循环陷阱

Wolfram语言编程思维:从表达式到函数式,告别循环陷阱

先说一个我经常在群里看到的现象。很多人学过Python或者C语言之后,转来用Wolfram语言(也就是Mathematica),第一反应往往是:这个语言怎么连循环都写得这么别扭?其实不是Wolfram别扭,而是写这个语…

2026/10/10 20:49:33 阅读更多 →
旅游景点评论数据挖掘实战:从5万条评论中定位游客抱怨与改进点

旅游景点评论数据挖掘实战:从5万条评论中定位游客抱怨与改进点

简介:这份资源面向数据分析初学者与旅游行业从业者,围绕去哪儿网国庆期间景点数据展开实战演练,帮助读者掌握从数据清洗到业务洞察的完整分析流程。压缩包共7个文件,以5个html可视化页面为主,辅以1个xlsx原始数据表和1…

2026/10/10 20:49:33 阅读更多 →
LabVIEW目录压缩实战:基于7-Zip命令行的完整方案

LabVIEW目录压缩实战:基于7-Zip命令行的完整方案

做上位机开发的人应该都遇到过这种需求:把测试数据、日志、配置文件打包成压缩文件,好归档、好传输。LabVIEW的文件I/O函数能读写单个文件,但要说把整个目录压成一个zip包,翻遍函数面板也找不到现成的VI。这个功能说大不大&#x…

2026/10/10 20:49:33 阅读更多 →
MySQL二进制数据读写实战:BLOB字段选型、写入读取与避坑指南

MySQL二进制数据读写实战:BLOB字段选型、写入读取与避坑指南

做开发这些年,但凡跟文件打交道的业务,迟早会撞上一个问题:图片、PDF、序列化对象这些二进制数据,到底要不要直接塞进数据库?“Mysql实战——二进制数据读写”这个标题看着朴素,背后其实是数据库设计里一个…

2026/10/10 20:48:32 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →