最稳方式是用 exec() 调用 mysql 命令行工具导入;需确保库存在、权限充足、SQL 文件为绝对路径且 PHP 进程可读;命令格式:mysql -u user -p'pass' -h host db_name < file.sql。

用 mysql 命令行导入 SQL 文件最稳
PHP 本身不提供“直接导入 SQL 文件”的内置函数,exec() 或 shell_exec() 调用系统命令才是生产环境里真正可靠的方式。别被网上那些用 fopen() + mysqli_query() 逐行执行的代码误导——遇到大文件、注释、多语句、存储过程时基本就崩。
实操建议:
- 确保目标数据库已存在,且用户有足够权限(
CREATE、INSERT、DROP等) - SQL 文件路径必须是绝对路径,PHP 进程能读取(注意 web 用户如
www-data的权限) - 命令格式:
mysql -u username -p'password' -h host database_name - 密码不建议明写在命令里,可用
mysql_config_editor配置登录路径,或改用--defaults-file指定配置文件 - 加
-f参数可跳过错误继续执行(慎用,可能掩盖结构问题)
mysqli_multi_query() 只适合小而干净的 SQL 片段
它确实能一次执行多个语句,但前提是 SQL 内容已加载进内存、不含注释、不含 DELIMITER、不含 USE 或 CREATE DATABASE 等跨库操作——这些都会导致解析失败或报错 Commands out of sync。
常见错误现象:
立即学习“PHP免费学习笔记(深入)”;
-
mysqli_multi_query()返回true,但后续mysqli_store_result()拿不到结果,mysqli_next_result()卡住 - 遇到
-- 注释或/* */直接报语法错误 -
INSERT INTO ... VALUES (...), (...), (...)这种批量插入在某些老版本 MySQL 中被截断
如果非要用,先做清洗:
- 用
file_get_contents()读取文件 - 用正则剔除注释:
preg_replace('/(-- .*)|(# .*)|(\/\*[\s\S]*?\*\/)/m', '', $sql) - 用
explode(';', $sql)拆分语句,再逐条mysqli_query()(不是multi_query) - 跳过空行和纯空格语句
Web 界面上传大 SQL 文件?别硬扛 PHP 配置
PHP 默认限制 upload_max_filesize = 2M、post_max_size = 8M、max_execution_time = 30,而一个中等规模的导出 SQL 很容易超 50MB。强行调高这些值不仅危险(耗尽内存、拖垮服务器),还可能被 Nginx/Apache 拦截。
更实际的做法:
- 前端用 JS 分片上传(如
axios+Blob.slice()),后端只接收并拼接文件,最后走第一种方式用mysql命令导入 - 上传后生成一个带时间戳的临时文件路径,由后台定时任务(
crontab)触发导入,避免阻塞 Web 请求 - 若必须同步响应,至少加
set_time_limit(0)和ini_set('memory_limit', '1G'),但仅限可信内网环境
导入失败时,先看 mysql --verbose 输出
加 -v 或 -vv 参数跑一遍命令,能立刻看到卡在哪条语句、报什么错。比在 PHP 里捕获 shell_exec() 的空返回或模糊异常有用得多。
几个高频陷阱:
- SQL 文件编码是
GBK,但数据库默认utf8mb4→ 导入后中文变问号,需加--default-character-set=utf8mb4 - 文件里含
SET FOREIGN_KEY_CHECKS=0;但末尾没恢复 → 后续写入报外键约束,得手动补SET FOREIGN_KEY_CHECKS=1; - MySQL 8.0+ 默认禁用
LOCAL INFILE,若 SQL 里有LOAD DATA LOCAL INFILE,要加--local-infile=1并在服务端开启对应选项
真正的难点从来不在“怎么调函数”,而在于 SQL 文件本身的不可控性:谁导出的、用什么工具、有没有手工改过、是否混用了不同版本语法。留一手日志记录原始命令和退出码,比写一百行容错逻辑都管用。



















