mysql还原sql文件时服务端内存耗尽卡死,根本原因是大事务不提交导致行锁/间隙锁堆积、脏页和undo日志占满buffer pool,触发oom killer或卡在元数据锁等待;小内存实例须严格控制事务粒度、禁用autocommit、合理设置buffer_pool_size并隔离relay_log路径。

还原 SQL 文件时内存耗尽导致锁表卡死,核心问题不在“还原命令本身”,而是服务端缓冲区、临时空间和事务粒度三者叠加压垮了小内存实例。加 --quick 或调大 innodb_buffer_pool_size 可能反而让情况更糟。
为什么 mysql 命令导入会触发 OOM 锁定?
不是客户端内存爆了,是 MySQL 服务端在解析和执行过程中不断申请内存:大事务不提交 → 行锁/间隙锁堆积 → innodb_lock_wait_timeout 触发前,innodb_buffer_pool 已被脏页和 undo 日志占满 → OS 触发 OOM Killer 干掉 mysqld 进程,或直接卡死在 Waiting for table metadata lock 状态。
典型现象包括:SHOW PROCESSLIST 里大量线程卡在 Updating 或 Locked;free -h 显示 available 长期低于 500MB;错误日志出现 The total number of locks exceeds the lock table size 或 OS error code 28: No space left on device(即使磁盘没满,也可能是 /tmp 的 tmpfs 耗尽)。
还原前必须检查的三个硬性条件
别急着跑 mysql -u root db ,先确认:
-
df -h /tmp和df -i /tmp—— 若/tmp是 tmpfs 且使用率 ≥95%,必须用--tmpdir=/data/tmp指向大分区 -
SELECT @@tmpdir, @@innodb_tmpdir;—— 看 MySQL 实际用哪个目录建临时表/排序,若指向/tmp,需同步改配置 -
cat /proc/$(pgrep mysqld)/status | grep VmRSS—— 查当前 mysqld 实际内存占用,若已超 1.2GB(对 2GB 总内存机器),说明 buffer pool + 其他线程已逼近极限
还原时必须拆解事务与锁粒度
原始 SQL 文件里一个 BEGIN; ... INSERT/UPDATE x 10w 行; COMMIT; 就是定时炸弹。正确做法是分段控制:
- 用
sed -n '/^INSERT INTO `t`/,/^;/p' backup.sql > t_inserts.sql抽出单表语句,再按每 1k 行切分(避免用split破坏 SQL 结构) - 导入时强制禁用自动提交:
mysql -u root -e "SET autocommit=0; SOURCE t_inserts_chunk1.sql; COMMIT;" db - 含 DDL(如
CREATE TABLE)的语句必须单独执行,且执行前加SET lock_wait_timeout = 10;防止建表锁住整个库 - 跳过视图、存储过程等元数据:
grep -v "^CREATE\( OR REPLACE\)\? VIEW\|^CREATE PROCEDURE\|^CREATE FUNCTION" backup.sql > clean.sql
小内存从库还原后最易忽略的致命点
很多人还原完就以为万事大吉,但以下两点不处理,几小时内就会复现锁死:
-
innodb_buffer_pool_size必须重设:云上 2GB 内存实例,不能沿用主库的 1.5G,应设为768M(满足128M × 6倍数),并确认free -h的available稳定 ≥1.8GB -
relay_log路径必须独立:还原过程常伴随主从同步,若relay_log和datadir共用分区,mysql-relay-bin.*文件瞬间打满磁盘,SQL 线程直接退出,错误日志只报No space left on device,找不到源头 - 还原后立刻执行:
STOP SLAVE; RESET SLAVE ALL;清空 relay log 索引,避免残留文件干扰后续同步











