14

当我使用 PHP 从资源(查询)中获取行时,我得到了一个非常奇怪、毫无意义且完全随机的错误。

我的开发机器是带有 Apache 2.2 的 Windows XP SP3,而 MySQL 在虚拟机上运行,​​使用 ubuntu 10.04,具有 768mb 内存、100GB 硬盘和 4 个逻辑内核(英特尔 q6600)。然而,这个问题与 Windows 上的 PHP 无关,因为我在数据库机器上运行代码时遇到了同样的错误。

我正在使用mysql扩展名(不是mysqlior mysqlnd),但是环顾四周,我创建了一个关于这个与 mysqlnd 扩展名相关的错误的补丁,所以,我可能应该尝试一下。

主要问题是当我执行这个查询(一个非常大的查询,有几个派生表和超过 20 个连接)并快速处理结果并且一切顺利时,但是当我的代码花费大约 15/20 秒来处理一个块时行数(我需要从它们之间以非常特殊的方式链接的行块构建一个对象,我无法更改它,数据库不是我的,并从这个对象制作一些 PDF)一段时间后(随机时间)我收到此错误“空行数据包正文”。

我使用无缓冲查询来减少内存消耗(如果启用缓冲,我会得到大约 260MB 的已用内存),但这应该不是问题。

4

7 回答 7

20

我遇到了同样的错误。我正在使用 PDO,但它应该基本上是一样的。

您是否在 MyISAM 表上进行操作?如果是这样,问题可能与此引擎使用的锁定模型有关:它锁定整个表,使用共享锁进行读取,使用排他锁进行写入。

这就是我想要做的:读取一个无缓冲的大型结果集,并更新同一个表中的一些行。由于您不能在同一连接上发布一个未缓冲结果集的语句,因此我尝试使用另一个连接进行更新。阅读一直很顺利,直到第一次更新,此时脚本停止了大约一分钟,然后我得到了“空行数据包正文”错误。

您会看到,当读取无缓冲时,共享锁会一直保留,直到读取整个结果集或关闭游标。在此期间,该表被共享锁锁定,因此其他连接可以获得该表上的共享锁(换句话说,从中读取),但排他锁(用于写入)将不得不等待。如果这发生在同一个脚本中,它将死锁。

现在,为了防止无限死锁,MySQL 将在一段时间后强制释放您的共享锁(IIRC 这受 table_lock_wait_timeout 的值影响),转储您的结果集并允许等待排他锁的写入语句轮到它。

因此,虽然在我的情况下,执行此操作的脚本是同一个脚本,因此在超时到期之前一直停滞不前,但也可能是其他一些脚本正在尝试对表进行具有相同效果的写操作,这可能是您的案子。

为我解决的问题是将表类型更改为 InnoDB,因为该引擎使用行级而不是表级锁。但是,由于您说数据库不是您的,因此您可能无法做到这一点。

于 2011-01-25T15:21:15.200 回答
9

实际问题是PHP 和 MySQL 之间的连接中断(=在 PHP接收到所有数据之前停止)。

当 PHP (PDO) 执行 MySQL 查询时,它会在打开的连接上发送查询,然后等待响应。响应由一组标头和一个正文组成,有点像 HTTP 请求。

如果在 PDO 尚未收到所有标头时连接中断,您将收到警告“读取结果集标头错误”,这意味着 PDO 无法解释响应,因为它只是部分(标头)。

如果在解析主体时连接断开,那么 PDO 将产生一个“空行数据包主体”,对应于您的错误。有关其他信息,请参阅此 Github PR

所以解决方法是找出连接被终止的原因:

  • 是因为连接超时吗?然后尝试按照建议修复配置
  • 是因为连接被`KILLè 命令手动终止了吗?然后要求凶手停止这样做
  • 是因为 Mysql 内存已满,而您的实例被您的虚拟主机杀死了吗?然后减小结果集大小/获得更大的 MySQL 服务器/请求更多 RAM
  • 是不是因为发生了“死锁”,所以 Mysql 任意杀死了一个连接(MyISAM 表更可能发生,包括内部临时表)?然后尝试使用 InnoDB
  • 是不是因为硬件连接中断了,比如 PHP 和 MySQL 之间的线路/wifi 接收不良?然后修复硬件。
  • 是因为转储程序要求终止处理转储的所有连接吗?然后等待现有连接完成,然后再运行转储
于 2019-10-04T12:39:49.720 回答
6

不久前有这个错误,我已经通过增加值来修复它

net_read_timeout = 360
net_write_timeout = 360

当一个连接在写入时打开,等待另一个查询结束以继续插入,这会超时,给出一个空行数据包。我正在处理非常大的数据集,使用价值超过 360。您的价值将取决于您的用例。

于 2017-11-08T13:58:35.790 回答
0

user589182 的回答很到位。我基本上在做同样的事情:读取一个无缓冲的大型结果集,并从同一个 PHP 脚本更新同一个表中的一些行。大约之后我收到了完全相同的错误消息。2500 次更新。从 MyISAM 切换到 InnoDB 后问题解决。

于 2011-03-07T05:49:40.380 回答
0

我遇到了同样的错误,并且在更新同一个表的行时,我还在读取一个无缓冲的大型结果集。

与其切换到 InnoDB,更好的解决方案可能是创建一个仅包含原始表主键的临时表。然后,遍历该临时表,同时从原始表中一次选择和更新一行。您必须使用两个单独的 MySQL 连接才能执行此操作,否则您将收到“命令不同步”错误。

您可能需要锁定原始表以防止其他人在发生这种情况时对其进行读/写。

于 2012-10-17T12:42:33.230 回答
0

我在 InnoDB 上,直到我切换编码环境(字面意思,就像位置)之前都没有遇到过这种类型的问题。因此,如果您更改了无线连接,则可能会出现问题,尤其是在公共场所时。

于 2016-05-07T18:39:11.807 回答
0

我现在遇到了同样的问题,正在阅读巨大的无缓冲查询。所有的表都是 InnoDB。

当另一个进程启动时它会停止mysqldump。所以这可能也是原因。

于 2018-10-14T06:40:19.507 回答