云服务器运维实战系列 · 第 4 篇(共 7 篇)
MySQL 迁移的三个坑:ibdata1、lower_case_table_names、socket
迁数据库时,我一开始图省事,想直接把旧机器的 MySQL 数据目录整个拷到新机器,结果服务怎么都起不来。对着错误日志一条条查,最后栽在三个地方:表空间文件、一个大小写参数、还有 socket 路径。
这三个坑都不复杂,但每一个都会让 MySQL 在启动阶段直接退出。这篇把报错和解法记下来。
坑一:直接拷 ibdata1,表空间初始化失败
我最初的做法是停掉 MySQL,把旧的数据目录(含 ibdata1、mysql 库、各业务库目录)复制到新机器的 /var/lib/mysql,然后启动。错误日志立刻报:

[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 必须初始化前定
解决了表空间,又冒出新报错:

[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,启动时一比对不一致,就拒绝启动。
正确处理是在初始化阶段就指定,而不是等服务装完再改配置。如果是全新初始化:
# 初始化时就带上参数(以实际配置方式为准)
sudo mysqld --initialize --lower-case-table-names=1 ...
我这边是导入已有数据,需要让新实例和旧实例保持一致:在配置文件 /etc/mysql/mysql.conf.d/mysqld.cnf 的 [mysqld] 段加上:
lower_case_table_names = 1
但务必在数据目录初始化/导入前设置;数据字典已经按 0 建好之后再改,同样起不来。改之前先确认旧库这个值:
# 旧机器上查
mysql -e "SHOW VARIABLES LIKE 'lower_case_table_names';"
坑三:socket 路径对不上
服务好不容易起来了,命令行连接又报:

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 在别的路径。
排查顺序:
# 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 文本,再到新库执行。文本不挑架构、不挑小版本,最稳:
# 旧机器:导出(含建库、建表、触发器等)
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 把存储过程、触发器、定时事件也带上。
迁移前的检查清单
- 新旧都查
SELECT VERSION();,大版本一致(同为 8.0)最稳; - 新旧都查
lower_case_table_names,新实例初始化前就设成一致; - 用
mysqldump导出,不复制 ibdata1 和数据目录; - 导入后核对库表数量,再验证站点连接;
- 连不上先看
systemctl status mysql和错误日志,再区分是服务没起还是 socket 路径问题。
数据库迁移,搬的是逻辑数据,不是物理文件——这一条记住,能省掉大半天对着启动报错发呆。