MySQL 迁移的三个坑:ibdata1、lower_case_table_names、socket

admin admin
2026-10-22 发布 | 2844 字 | 预计 8 分钟 | 4 次阅读 | 0 条评论

云服务器运维实战系列 · 第 4 篇(共 7 篇)

MySQL 迁移的三个坑:ibdata1、lower_case_table_names、socket

迁数据库时,我一开始图省事,想直接把旧机器的 MySQL 数据目录整个拷到新机器,结果服务怎么都起不来。对着错误日志一条条查,最后栽在三个地方:表空间文件、一个大小写参数、还有 socket 路径。

这三个坑都不复杂,但每一个都会让 MySQL 在启动阶段直接退出。这篇把报错和解法记下来。

坑一:直接拷 ibdata1,表空间初始化失败

我最初的做法是停掉 MySQL,把旧的数据目录(含 ibdata1、mysql 库、各业务库目录)复制到新机器的 /var/lib/mysql,然后启动。错误日志立刻报:

复制物理数据目录,版本差异可能导致启动失败。
复制物理数据目录,版本差异可能导致启动失败。
Text
Text
[ERROR] [InnoDB] The Auto-extending innodb_system data file './ibdata1' is of a different
size ... than specified in the cnf file: initial 768 pages, max 0 ...
[ERROR] [InnoDB] Plugin initialization aborted with error Generic error.
[ERROR] [Server] Failed to initialize DD Storage Engine
[ERROR] [Server] Data Dictionary initialization failed.
[ERROR] [Server] Aborting

ibdata1 是 InnoDB 的系统表空间文件,里面有数据字典、change buffer 等关键信息。它的初始大小、页结构、内部记录的格式,跟 MySQL 版本、编译参数、配置强相关。我新旧两边 MySQL 小版本不同(一边 8.0.45、一边 8.0.46),文件对不上,InnoDB 读到的结构和它预期的不一致,直接拒绝初始化。

我一度想靠在配置里写 innodb_data_file_path = ibdata1:10M:autoextend 去对齐,但这个参数是初始化时定的,对一个已经存在、内容不匹配的文件没用,反而因为重定向写错位置(没加 sudo、文件落到了 /root)白折腾。

结论:物理拷贝数据目录这条路,跨版本、跨架构基本走不通。 正确做法是逻辑迁移(见文末)。

坑二:lower_case_table_names 必须初始化前定

解决了表空间,又冒出新报错:

新库初始化之前就要对齐表名大小写设置。
新库初始化之前就要对齐表名大小写设置。
Text
Text
[ERROR] [Server] Different lower_case_table_names settings for server ('0') and data dictionary ('1').
[ERROR] [Server] Data Dictionary initialization failed.

lower_case_table_names 控制库名、表名的大小写敏感:

  • 0:按你创建时的大小写存,查询区分大小写(Linux 默认);
  • 1:统一转小写存储,查询不区分大小写(Windows 默认)。

关键点:这个参数必须在 MySQL 数据目录初始化(initialize)之前就定好,初始化之后不能改。旧数据字典里记录的是 1,新服务器默认 0,启动时一比对不一致,就拒绝启动。

正确处理是在初始化阶段就指定,而不是等服务装完再改配置。如果是全新初始化:

Bash
Bash
# 初始化时就带上参数(以实际配置方式为准)
sudo mysqld --initialize --lower-case-table-names=1 ...

我这边是导入已有数据,需要让新实例和旧实例保持一致:在配置文件 /etc/mysql/mysql.conf.d/mysqld.cnf 的 [mysqld] 段加上:

ini
INI
lower_case_table_names = 1

但务必在数据目录初始化/导入前设置;数据字典已经按 0 建好之后再改,同样起不来。改之前先确认旧库这个值:

Bash
Bash
# 旧机器上查
mysql -e "SHOW VARIABLES LIKE 'lower_case_table_names';"

坑三:socket 路径对不上

服务好不容易起来了,命令行连接又报:

核对服务状态与 socket 路径,必要时改走 TCP。
核对服务状态与 socket 路径,必要时改走 TCP。
Text
Text
ERROR 2002 (HY000): Can't connect to local MySQL server through socket
'/var/run/mysqld/mysqld.sock' (2)

本地用 mysql 命令连接时,默认走 Unix socket 文件,路径编译时定死。报错末尾的 (2) 是系统错误码 ENOENT,意思是这个 socket 文件不存在——要么服务没真正起来,要么实际 socket 在别的路径。

排查顺序:

Bash
Bash
# 1. 确认服务真的在运行
systemctl status mysql

# 2. 找实际的 socket 文件在哪
sudo find / -name "mysql*.sock" 2>/dev/null

# 3. 看配置里指定的 socket
grep -rn "socket" /etc/mysql/

常见情况:

  • 服务没起来(前两个坑没解决),socket 自然不存在——回到错误日志继续查;
  • socket 在别的路径,连接时显式指定:mysql -S /run/mysqld/mysqld.sock;
  • 或干脆走 TCP:mysql -h 127.0.0.1 -P 3306 -u root -p。

正确的迁移方式:逻辑导出导入

物理文件不能搬,就用 mysqldump 把数据导成 SQL 文本,再到新库执行。文本不挑架构、不挑小版本,最稳:

Bash
Bash
# 旧机器:导出(含建库、建表、触发器等)
mysqldump -u root -p --single-transaction --routines --triggers --events \
  --databases 你的库名 > backup.sql

# 拷贝 backup.sql 到新机器后导入
mysql -u root -p < backup.sql

--single-transaction 让 InnoDB 在不锁表的情况下拿到一致性快照;--routines --triggers --events 把存储过程、触发器、定时事件也带上。

迁移前的检查清单

  1. 新旧都查 SELECT VERSION();,大版本一致(同为 8.0)最稳;
  2. 新旧都查 lower_case_table_names,新实例初始化前就设成一致;
  3. 用 mysqldump 导出,不复制 ibdata1 和数据目录;
  4. 导入后核对库表数量,再验证站点连接;
  5. 连不上先看 systemctl status mysql 和错误日志,再区分是服务没起还是 socket 路径问题。

数据库迁移,搬的是逻辑数据,不是物理文件——这一条记住,能省掉大半天对着启动报错发呆。

全文完
全站文章均为原创,转载请保留署名和出处。
阅读 4 次