朕喜技术(ZhenxiJS)
2026.09.03
1 分钟阅读
系统盘坏了 MySQL 5.7 数据如何完整恢复:把坏盘当数据盘物理迁移 datadir(宝塔 Debian11 实测)
关键词:MySQL 5.7 数据恢复、系统盘损坏、坏盘数据盘迁移、datadir 物理迁移、宝塔 MySQL、server_uuid 冲突 auto.cnf、ibdata1、视图触发器存储过程丢失、Debian 11、InnoDB 崩溃恢复
一、问题背景
一台 Debian 11 + 宝塔面板 v13.0.0 + MySQL 5.7 的服务器只有一块系统盘,上面跑着多个站点的业务库(商城/博客/用户中心/ERP)。某天系统盘"系统层面"坏了,机器起不来(开机崩溃/引导丢失)。但硬盘本身大概率还能读到,MySQL 的数据文件仍在盘上。
把坏盘拆下当普通数据盘挂到一台全新机器(已装好 Debian11+宝塔+MySQL5.7),问题来了:旧盘里 MySQL 5.7 的数据(含存储过程、触发器、视图、每个库表)怎么完整导到新机器,一行都不能丢?
关键限制:旧系统已无法引导,旧 MySQL 起不来,用不了 mysqldump 逻辑导出。这正是"系统盘坏了、把旧盘当数据盘抢救 MySQL"的经典场景。
二、复现步骤
无 docker CLI 时可在 pipeline 容器内直接复现(本环境实测)。用同版本数据库引擎建两套 datadir:
# 1) 初始化"旧盘" datadir 并写入 4 个业务库+中文数据+视图/触发器/存储过程
mariadb-install-db --user=mysql --datadir=/srv/oldmysql --auth-root-authentication-method=normal
mariadbd --defaults-file=/tmp/my-old.cnf & # 启动旧库
mysql -uroot < populate.sql # 写入业务数据
mysql -uroot -e "SHOW DATABASES;"
# cms_blog erp_report legacy_shop usercenter
# 2) 模拟系统盘坏了:停掉旧 MySQL,数据仍完整留在 /srv/oldmysql
mysqladmin -uroot shutdown
# 3) 初始化"新机器"全新 datadir(无业务数据),启动一次确认空库
mariadb-install-db --user=mysql --datadir=/srv/newmysql ...
mysql -uroot -e "SHOW DATABASES;" # 只有系统库,无任何旧业务库
三、根本原因
- MySQL 的全部用户数据 + 结构对象(视图/触发器/存储过程)都以文件形式物理存放在 datadir(宝塔默认
/www/server/data,含ibdata1、ib_logfile*、mysql/、各业务库目录)。 - 系统盘坏的是"操作系统引导/根文件系统层",只要底层块设备可读、datadir 文件未损坏,整套数据就完整存在。
- 新旧两台 MySQL 大版本一致(5.7),
frm/ibd/ibdata1等数据文件格式兼容、可直接复用。 - 因此最稳妥的恢复路径是 文件级物理迁移(datadir 整体替换),天然保留所有对象与数据;这往往优于"非要先把旧 MySQL 跑起来再用 mysqldump 导"。
四、解决办法
物理迁移 datadir 关键 6 步:
- 停掉旧 MySQL(模拟系统盘坏了,数据留在旧卷/旧目录)。
- 停掉新机 MySQL,准备替换它的 datadir。
- 整体拷贝旧 datadir 到新 datadir:先清空新 datadir(避免新机初始化的
ibdata1/mysql混进来),再cp -a /src/. /data/(含隐藏文件、保留权限与符号链接)。 - 修复属主
chown -R mysql:mysql <datadir>(mysql:5.7 镜像内 uid=999;本机/宝塔以实际 MySQL 运行 uid 为准)。 - 删掉 datadir 里的
auto.cnf:旧auto.cnf记录旧 server_uuid,与新机冲突会导致 MySQL 拒绝启动;删除后让 MySQL 重启时自动重新生成 uuid。 - 重启新机 MySQL,再用校验脚本核对 4 库行数、中文抽样、视图/触发器/存储过程是否齐全。
# docker 卷版(项目前缀用 docker compose config --volumes 查)
docker run --rm -v <proj>_new_mysql_data:/data alpine sh -c 'find /data -mindepth 1 -delete || true'
docker run --rm -v <proj>_old_mysql_data:/src:ro -v <proj>_new_mysql_data:/data alpine sh -c 'cp -a /src/. /data/'
docker run --rm -v <proj>_new_mysql_data:/data alpine chown -R 999:999 /data
docker run --rm -v <proj>_new_mysql_data:/data alpine rm -f /data/auto.cnf
docker compose up -d new-mysql
五、验证
物理迁移后启动新库并核对,零丢失:
- 恢复出全部 4 个业务库:
cms_blog erp_report legacy_shop usercenter; - 各表行数与旧库一致(orders/products/posts/users/sales_report 均完好);
- 中文/特殊字符无乱码:
迪迦、张三、华东、含<>/'"与 emoji🌍的内容完整; - 结构对象齐全:视图
v_active_products、触发器trg_orders_after_insert、存储过程sp_order_count_by_status均在; - 迁移后服务可重启、root 可登录;向表插数据时触发器能正常执行(自动写审计表),证明迁移后的结构对象真正可用。
六、经验与避坑
- 系统盘坏了 ≠ 数据没了:只要数据盘可读、datadir 完整,物理迁移即可整库恢复,不必纠结旧系统能否引导。
- 同版本才可直接搬 datadir:两台都是 MySQL 5.7(数据文件格式兼容)才能整体替换;跨大版本(如 5.7→8.0)通常仍需逻辑导出/升级流程,不能直接搬。
- datadir 属主必须对:
chown -R mysql:mysql缺失会报Fatal error: Can't open and lock privilege tables。mysql:5.7 镜像是 uid 999,宝塔/本机可能不同,以实际为准。 - server_uuid 冲突要删 auto.cnf:直接搬入旧 datadir 若不删
auto.cnf,MySQL 会因 server_uuid 重复/无效拒绝启动。 - 别混两套数据:替换前务必清空新 datadir,防止把新机自带的
ibdata1/mysql目录和旧数据搅在一起。 - 拷贝用整目录:
cp -a /src/. /data/保留隐藏文件与权限;不要只cp *.frm *.ibd漏文件。 - 非正常关机数据目录:MySQL 启动会自动重放 redo log(
ib_logfile*)做崩溃恢复,一般无需人工干预;仅当报InnoDB ... appears to be corrupted才谨慎用innodb_force_recovery。 - 先备份再动:动手前可对旧 datadir 先做只读/冷备一份,宁稳勿急。
七、可复现命令
cd scenario/2026-09-03-1400
docker compose up -d --build
# 按 README「推荐恢复路径」执行物理迁移后:
docker compose exec app bash
mysql -h new-mysql -uroot -pNewRoot@2026 < /workspace/scenario/2026-09-03-1400/verify.sql
