做批量写入时最常见的两个报错——ERROR 1153 (08S01): Got a packet bigger than 'max_allowed_packet' bytes 和莫名其妙的 ERROR 2006 (HY000): MySQL server has gone away——大概率是同一个原因:单条 SQL 语句超过了 max_allowed_packet 的限制。本文整理这个参数的默认值、三种修改方式(配置文件 / SET GLOBAL / SET PERSIST)、客户端侧同样要改的地方,以及改完之后常见的「为什么没生效」。
一、max_allowed_packet 是什么
max_allowed_packet 限制的是单个网络包的大小,实际效果就是:一条 SQL 语句、一行数据、一个 binlog event 的最大字节数。批量插入几百上千行、写入大文本 / BLOB、构造超长 IN (...) 列表时,整条语句会超限被拒。
默认值随版本变化,动手前先用 SHOW VARIABLES 确认真实值:
| 版本 | 默认值 | 说明 |
|---|---|---|
| MySQL 5.7 | 4 MB | 最容易撞到限制的版本 |
| MySQL 8.0 | 64 MB | 默认值已放宽,但大批量写入仍可能不够 |
| MariaDB 10.2.4+ | 16 MB | 与 MySQL 取值不同,需单独确认 |
几个硬边界:
- 取值范围
1024字节 ~1073741824(1 GB),超过 1 GB 按 1 GB 处理。 - 不是 1024 整数倍的值会被向下取整到 1024 的倍数。
- 该变量同时有 global 和 session 两个作用域,且是动态变量,可以不重启修改。
二、方式一:修改配置文件(推荐,永久生效)
在配置文件的 [mysqld] 段中加入(注意单位可以直接写 M / G):
[mysqld]
max_allowed_packet=100M100M 等价于 104857600。也可以直接写字节数:
[mysqld]
max_allowed_packet=104857600各平台配置文件位置:
| 平台 | 常见路径 |
|---|---|
| Windows | my.ini,通常在 C:\ProgramData\MySQL\MySQL Server 8.0\(ProgramData 是隐藏目录),或安装目录下 |
| CentOS / RHEL | /etc/my.cnf |
| Debian / Ubuntu | /etc/mysql/my.cnf,实际配置常拆分在 /etc/mysql/mysql.conf.d/mysqld.cnf |
| macOS(Homebrew) | /opt/homebrew/etc/my.cnf 或 /usr/local/etc/my.cnf |
不确定当前实例读的是哪份配置时,可以查启动参数:
SHOW VARIABLES LIKE 'datadir'; -- 顺带确认实例身份
-- 或直接看进程启动参数中的 --defaults-file
-- Windows: 服务属性 → 可执行文件路径;Linux: ps -ef | grep mysqld改完必须重启 MySQL 服务(Windows 下 net stop mysql80 && net start mysql80,或 services.msc 里重启服务;Linux 下 systemctl restart mysqld)。
三、方式二:SET GLOBAL(临时,立即生效)
不重启、马上生效:
SET GLOBAL max_allowed_packet = 1073741824;两个必须知道的限制:
- 只对新建立的连接生效,已经连上的连接(以及连接池里空闲的那些)仍然用旧值,需要重连或重启应用。
- 重启后失效,属于一次性救急手段,长期配置还是要落到配置文件。
也可以只改当前会话(不能超过 global 值):
SET SESSION max_allowed_packet = 33554432;四、方式三:SET PERSIST(MySQL 8.0,改完即持久化)
MySQL 8.0 提供了折中方案:既立即生效,又写入 mysqld-auto.cnf 在重启后保留,不用手工编辑配置文件。
SET PERSIST max_allowed_packet = 1073741824;需要 SYSTEM_VARIABLES_ADMIN 权限。不想要这条持久化配置时用 RESET PERSIST max_allowed_packet; 清除。
三种方式对比
| 方式 | 是否立即生效 | 是否重启保留 | 适用场景 |
|---|---|---|---|
| 配置文件 + 重启 | 否 | 是 | 常规配置,最稳妥 |
SET GLOBAL |
是(仅新连接) | 否 | 线上救急、临时放宽 |
SET PERSIST(8.0) |
是(仅新连接) | 是 | 不想重启又要长期生效 |
五、客户端侧也要同时放宽
实际可用的包大小取客户端与服务端的较小值,只改服务端可能仍然报错。命令行工具需要显式指定:
# mysql 客户端
mysql --max-allowed-packet=256M -u root -p
# mysqldump 导出/恢复含大字段的数据
mysqldump --max-allowed-packet=256M -u root -p db_name > backup.sql
# 也可以写进配置文件,让客户端工具自动读取
[mysqldump]
max_allowed_packet=256M应用侧注意三点:
- JDBC(Connector/J)有同名的
maxAllowedPacket连接参数,默认值随驱动版本变化,需要时在连接串里显式指定。 - 服务端用
SET GLOBAL放宽后,连接池中的存量连接不会自动更新,要让应用重启或等连接自然轮换。 - 批写入本身也建议控制单批条数(如每批 500~1000 行),把「单条语句不要太大」当成常规约束,比单纯把参数调到 1 G 更稳。
六、验证与排查
-- 查看当前有效值
SHOW VARIABLES LIKE 'max_allowed_packet';
SELECT @@global.max_allowed_packet, @@session.max_allowed_packet;
-- 查看是否被 SET PERSIST 写入了持久化配置(8.0)
SELECT * FROM performance_schema.persisted_variables;常见「改了没生效」的原因:
- 改完没重启,或只做了
SET GLOBAL却在旧连接上继续跑。 - 配置段写错:写进
[client]/[mysql]只影响客户端,服务端必须放在[mysqld]。 - 改错了配置文件:机器上有多份
my.ini/my.cnf,实例实际读的不是正在编辑的这一份,以启动参数--defaults-file为准。 - 只改了服务端:客户端工具或驱动仍是旧值,取较小值后依旧报错。
另外两点容易被忽略:
- 主从 / 组复制环境:从库的
max_allowed_packet不应小于主库,否则大事务对应的 binlog event 在从库上会应用失败,建议全集群统一取值。 - 不要盲目设 1 G:缓冲区按需分配、不是每个连接都立刻占用,但极端情况下会给单连接带来更大的内存峰值。按「实际最大单条语句的 2~4 倍」估算即可,比如峰值语句 20 MB,设置
64M已经足够。
七、速查卡
# Linux: /etc/my.cnf 或 /etc/mysql/mysql.conf.d/mysqld.cnf
# Windows: my.ini(常在 C:\ProgramData\MySQL\MySQL Server 8.0\)
[mysqld]
max_allowed_packet=100M # 100M = 104857600 字节,重启生效SET GLOBAL max_allowed_packet = 1073741824; -- 立即生效,仅新连接,重启失效
SET SESSION max_allowed_packet = 33554432; -- 仅当前会话
SET PERSIST max_allowed_packet = 1073741824; -- 8.0,重启保留
SHOW VARIABLES LIKE 'max_allowed_packet'; -- 验证mysql --max-allowed-packet=256M -u root -p # 客户端侧同步放宽
mysqldump --max-allowed-packet=256M -u root -p db > backup.sql
评论区