bigywater.tech
常见报错 · 6

MySQL ERROR 1153 Got a packet bigger than max_allowed_packet:PyMySQL 里看到的却是 2006,bytes 参数只能放一半

“Got a packet bigger than max_allowed_packet bytes”(ERROR 1153)是写入大字段、批量插入、导入 dump 时的常见报错,但很多人在 Python 里根本看不到 1153,只看到 2006 server has gone away。这篇用 1 MiB 的服务器上限在本机复现,看清楚到底差多少字节、调大要怎么调、批量插入为什么有时能躲过去。

2006
服务器上限 1 MiB 时 PyMySQL 插入 2 MB 字符串得到的错误码(命令行客户端同一场景是 1153)
524271
PyMySQL 的 bytes 参数在 1 MiB 上限下最多能放的字节数(str 参数是 1048543),因为 bytes 被拼成十六进制
1024000
PyMySQL cursor.max_stmt_length:executemany 切分多行 INSERT 的阈值;服务器上限小于它时 executemany 也会 2006

1153 与 2006

实测 · 本机执行输出
  实例二默认 @@max_allowed_packet = 67108864(64 MiB);实例一 = 1048576
  PyMySQL insert 100 KB 的字符串 -> ok
  PyMySQL insert 2 MB 的字符串 -> OperationalError: (2006, "MySQL server has gone away (BrokenPipeError(32, 'Broken pipe'))")
  同一个连接接着执行 select 1 -> InterfaceError: (0, '')
  命令行: mysql d < big.sql(一条 2097185 字节的 INSERT)-> ERROR 1153 (08S01) at line 1: Got a packet bigger than 'max_allowed_packet' bytes
  命令行: select 1 对比 -> 1
  表里的行数: 1(只有那次成功的 100 KB)
  服务器错误日志里含 packet 的行: (没有)

服务器遇到超限的包会直接断开连接,命令行客户端能读到 1153,PyMySQL 往往只看到 2006 加 Broken pipe,之后同一个连接不可用。

上限是多少

实测 · 本机执行输出
  二分查找:整条 SQL 文本最长 1048574 字节还能执行(上限 1048576,差 2)
  insert 一个 str 参数:最大 1048543 个字符(整条语句 = 数据 + 31 字节)
  insert 一个 bytes 参数:最大 524271 字节,约是上面的一半;原因:PyMySQL 拼出的 SQL 是 "insert into b(data) values (X'616263')"
  str 里全是单引号:最大 524271 个字符(每个单引号在 SQL 里要写成两个字符)

整条 SQL 文本最长比 max_allowed_packet 少 2 字节;bytes 参数被拼成十六进制,有效数据只有一半;单引号这类要转义的字符也会翻倍。

怎么调大

实测 · 本机执行输出
  SET GLOBAL max_allowed_packet = 4194304 -> []
  已经连着的老连接: @@session.max_allowed_packet = 1048576,@@global = 4194304
  老连接放 2 MB 的字符串 -> OperationalError: (2006, "MySQL server has gone away (BrokenPipeError(32, 'Broken pipe'))")
  新连接: @@session.max_allowed_packet = 4194304
  新连接 SET SESSION max_allowed_packet = 8388608 -> OperationalError: (1621, "SESSION variable 'max_allowed_packet' is read-only. Use SET GLOBAL to assign the value")
  新连接放 2 MB 的字符串 -> ok
  另一个新连接放 5 MB 的字符串 -> OperationalError: (2006, "MySQL server has gone away (BrokenPipeError(32, 'Broken pipe'))")

SET GLOBAL 只对之后的新连接生效,老连接要重连;会话级变量只读,SET SESSION 报 1621;SET GLOBAL 重启后失效。

批量插入

实测 · 本机执行输出
  PyMySQL cursor.max_stmt_length = 1024000
  服务器上限 1 MiB:executemany 50000 行(每行 100 字节,约 5 MB)-> ok,0.25 s,表里 50000 行
  把服务器上限改成 524288(比 max_stmt_length 小一半)后同样的 executemany -> OperationalError: (2006, "MySQL server has gone away (BrokenPipeError(32, 'Broken pipe'))")
  手写一条 9000 行的多行 INSERT -> ok(945024 字节)
  手写一条 11000 行的多行 INSERT -> OperationalError: (2006, "MySQL server has gone away (BrokenPipeError(32, 'Broken pipe'))")(语句 1155024 字节)
  对照:同样 11000 行,用命令行客户端读文件执行 -> ERROR 1153 (08S01) at line 1: Got a packet bigger than 'max_allowed_packet' bytes

PyMySQL 的 executemany 在 max_stmt_length(1024000)处切分;服务器上限不小于它时安全,小于它时仍然 2006。手写超长的多行 INSERT 没有这层保护。

客户端的限制

实测 · 本机执行输出
  默认服务器上有一行 20 MiB 的数据: ((1, 20971520),)
  命令行客户端 --help 里 max-allowed-packet 的默认值: 16777216(16 MiB)
  命令行客户端用默认值读这一行 data -> ERROR 2020 (HY000) at line 1: Got packet bigger than 'max_allowed_packet' bytes
  命令行客户端 --max-allowed-packet=33554432(32 MiB)读 -> 读到 20971520 字节
  命令行客户端 --max-allowed-packet=1048576 只读 length(data) 这个数字 -> 20971520
  PyMySQL 读同一行(它的 Connection.max_allowed_packet = 16777216)-> 读到 20971520 字节

ERROR 2020 是客户端侧的 max_allowed_packet,命令行客户端默认 16 MiB,读超过它的单行会失败,改服务器的值没用。

分步演示

包太大
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / MySQL 26.10.0 与 Redis 8.10.2(Homebrew,每个实验自己起实例,端口 9150 以上,只监听 127.0.0.1,不碰本机已有的实例)/ Python 3.15.0 + PyMySQL(按各篇注明用到哪些及版本);报错文本都是真实复现时原样捕获的;计时用 time.perf_counter,数字每次运行会有小幅抖动。本篇用两个自己起的 MySQL 26.10.0 实例(一个 --max-allowed-packet=1048576,一个默认 67108864),PyMySQL 2.2.8 的 unix socket 连接和 Homebrew 的 mysql 命令行客户端 26.10.0;大小用二分查找实测
    • 没有验证:SET PERSIST 与 my.cnf 的写法在重启后的表现;max_allowed_packet 的最大值;JDBC(Connector/J)、mysqlclient 等驱动遇到超限时报什么;mysqldump 导出 --max-allowed-packet 与导入的配合;replication 中 replica_max_allowed_packet 与 binlog 事件的限制;PyMySQL 读取不拦大包的具体原因(只观察到读到了 20971520 字节)
    京ICP备2026067502号-1京公网安备11010502063487号