使用mysql_fetch_object时出现奇怪的MySQL错误“空行数据包正文”(PHP 5.3.3)

时间:2010-09-23 16:31:12

标签: php mysql php-5.3

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

我的开发机器是带有Apache 2.2的Windows XP SP3,而MySQL运行在虚拟机上,使用ubuntu 10.04,带有768mb的RAM,100GB的HDD和4个逻辑核心(Intel q6600)。但是这个问题与Windows上的PHP无关,因为当我在数据库机器上运行代码时遇到同样的错误。

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

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

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

7 个答案:

答案 0 :(得分:18)

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

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

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

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

现在,为了防止无休止的死锁,MySQL会在一段时间后强行释放你的共享锁(IIRC这会受到table_lock_wait_timeout的值的影响),转储你的结果集并允许带有等待的独占锁的写语句轮到它

所以,虽然在我的情况下,它是相同的脚本,并且因此停止直到超时到期,也可能是某些其他脚本正在尝试对表执行写操作具有相同的效果,这可能是什么在你的情况下发生了。

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

答案 1 :(得分:6)

一段时间以前出现此错误,我通过增加

的值来修复它
net_read_timeout = 360
net_write_timeout = 360

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

答案 2 :(得分:0)

user589182的回答是现货。我基本上做同样的事情:读取一个没有缓冲的大结果集,并从同一个PHP脚本更新同一个表中的一些行。我得到完全相同的错误信息约。 2500更新。从MyISAM切换到InnoDB后问题解决了。

答案 3 :(得分:0)

我得到了同样的错误,我在更新同一个表的行时也读取了一个没有缓冲的大结果集。

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

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

答案 4 :(得分:0)

我在InnoDB上,在切换编码环境(字面意思,就像位置)之前从未遇到过这种类型的问题。因此,如果您更改了无线连接,这可能是问题,特别是如果它是一个公共场所。

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

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

当PHP(PDO)执行MySQL查询时,它将在打开的连接上发送查询,然后等待响应。响应由一组标头和主体组成,某种程度上类似于HTTP请求。

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

如果在解析正文时连接断开,则PDO将生成一个“空行数据包正文”,与您的错误相对应。有关其他信息,请参见this Github PR

因此,解决方法是查找连接被终止的原因:

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