【问题标题】:PHP massive memory usage for SQL query用于 SQL 查询的 PHP 大量内存使用
【发布时间】:2012-08-08 11:21:20
【问题描述】:

在优化我的 Apache + PHP 内存使用时,我偶然发现了一个奇怪的问题。基本上,当尝试绑定 MySQLi 查询的结果时,代码会出现错误消息“致命错误:允许的内存大小为 16777216 字节已用尽(尝试分配 50331646 字节)”。

相关表格有:

CREATE TABLE `note` (
  `noteID` int(11) NOT NULL AUTO_INCREMENT,
  `contentID` int(11) NOT NULL,
  `text` mediumtext NOT NULL,
  PRIMARY KEY (`noteID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `content` (
  `contentID` int(11) NOT NULL AUTO_INCREMENT,
  `text` varchar(2048) NOT NULL,
  `datestamp` datetime NOT NULL,
  PRIMARY KEY (`contentID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

而爆炸的查询是:

select content.contentID, 
content.text, 
content.datestamp, 
note.noteID, 
note.contentID, note.text 
from basereality.content as content 
inner join basereality.note as note 
on content.contentID = note.contentID 
where content.contentID = 1028 ;

在服务器上的 MySQL 中运行查询运行良好,返回的 'note' 大小小于一千字节。

一个奇怪的事情是它试图分配 50331646 的大小是十六进制的 0x2FFFFFE,这是一个可疑的整数。这几乎就像 PHP 试图分配一个足够大的缓冲区来容纳可能的最大的 mediumtext 字段,而不是实际为检索到的数据分配内存。

除了增加 PHP 中允许的最大内存之外,任何人都知道解决方法吗?

顺便说一句,其他人也有同样的问题,但似乎不知道如何解决。 Out of memory (allocated 50855936) (tried to allocate 50331646 bytes)

爆炸的确切线路是:

$statement->bind_result(
  $content_contentID,
  $content_text,
  $content_datestamp,
  $note_noteID,
  $note_contentID,
  $note_text);

将查询更改为选择“'A test string' as note”,而不是获取列不再显示大量内存使用情况。

顺便说一句,我确定我不是在尝试检索大数据。这显示了 note 表中 mediumtext 字段的实际长度:

 select length(text) from basereality.note;
+--------------+
| length(text) |
+--------------+
|          938 |
|          141 |
|         1116 |
|          431 |
|          334 |
+--------------+

【问题讨论】:

  • 您能显示出现此错误的 PHP 代码的确切行吗?
  • 完成 - 不要认为它会有所帮助。
  • 您是否 1000% 确定您要获取的所有数据库字段都不大于 50MB?
  • 我进行了一个简短的测试来重现这一点:Windows 上的 PHP 5.3 启用 mysqlnd 作为数据库底层连接器的替代品,没有错误或增加内存使用量。 Linux 上的 PHP 5.3 默认不启用它,并且观察到内存使用量增加(它没有崩溃,因为 memory_limit 远高于 16 MB,但内存使用量约为 17 MB)。阅读 PHP 源代码让我想到在 $stmt->execute() 之前尝试 $stmt->attr_set(MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH, 1);,这使内存使用率恢复到正常水平。
  • DCoder - 我也检查了 MySQLi 源。该属性实际上是 long 而不是 bool bugs.mysql.com/bug.php?id=16144 但 PHP 显然不支持将其设置为 1 以外的任何值。 bugs.php.net/bug.php?id=62798

标签: php apache memory mysqli


【解决方案1】:

我找到了答案并在另一个问题中发表了评论:

Allowed memory size of 67108864 bytes exhausted

基本上,在旧的 MySQL 连接器库中,不只是分配字段 'text' 的实际大小来存储结果,而是分配类型 mediumtext 的最大可能大小。

即即使结果字段只有 100 KB,也始终分配 16 兆字节。

解决此问题的最简单且可能最好的方法是改用新的 MySQLND 连接器,它不会显示此行为。

【讨论】:

    【解决方案2】:

    你试过mysqli_store_result吗? 查看下面的 php.net 链接,更多人有同样的问题,有些人通过在绑定结果之前调用 store_result 解决了这个问题。

    http://php.net/manual/en/mysqli.store-result.php

    http://dev.mysql.com/doc/refman/5.0/en/mysql-stmt-store-result.html

    【讨论】:

    • 感谢您的建议,但我的 MySQL 代码是从表定义中自动生成的,因此当存在 blob 或文本字段时,将其切换为使用不同的方式访问表并不容易。
    猜你喜欢
    • 1970-01-01
    • 2018-07-14
    • 1970-01-01
    • 2016-04-01
    • 2021-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多