为啥 PHP 使用大量内存来存储查询结果

Posted

技术标签:

【中文标题】为啥 PHP 使用大量内存来存储查询结果【英文标题】:Why is PHP using a lot of memory to store a query result为什么 PHP 使用大量内存来存储查询结果 【发布时间】:2021-01-26 20:40:59 【问题描述】:

我使用Laravel 8 直接使用query buildermysql 8 表执行查询以避免Eloquent 开销,但无论如何我都会消耗大量内存。 为了向您展示一个示例,我执行以下查询以准确选择 300 000 个元素。

我的代码如下所示:

$before = memory_get_usage();
$q_coords = DB::table('coords')->selectRaw('alt, lat, lng, id')
    ->where('active', 1)->take(300000)->get();

$after = memory_get_usage();
echo ($after - $before);

如果我没记错的话,它会显示 169760384 这意味着类似于 169MB.. 对我来说看起来很多,因为在我的查询中,我只要求 2 float2 bigInt,它们代表 4 x 8 字节(32 字节强>)。 而且.. 32 x 300 000 条记录 ~= 9600000(几乎 10MB)。

它怎么可能使用这么多内存?我很惊讶。

编辑

我也试过直接使用PDO,结果一样。

$query = DB::connection()->getPdo()->query("select alt, lat, lng, id from coords WHERE active = 1 LIMIT 300000");
$q_coords = $query->fetchAll();

【问题讨论】:

你的问题到底是什么?原因 300 000 个条目对于任何代码都不是正常行为。 + 在 php 中,每个变量都被构造为一个 zend 对象 + 每个 Eloquent 的结果都被转换为一个 PHP 对象... @N69S 我没有使用Eloquent 来避免Models 造成的开销,而是使用stdClass。我得到 300k 条目以稍后执行一些计算,但在这里我只是想了解为什么它确实使用那么多内存来进行个人理解,而我期望它使用更少(因为我知道我可以使用 chunkscursors 无论如何)。处理 32 字节的记录似乎有很多“周围”的东西.. 如果你真的需要在单个查询中访问300.000条记录,那么尝试直接使用pdo并使用PDO::FETCH_NUM get()->toArray() 怎么样? @Marc 所以这个->where('active', 1)->take(300000)->get() 不是很有说服力吗? 【参考方案1】:

因为它们在内存中被表示为 PHP 对象,而不仅仅是它们的原始数据使用。 但是有一个限制内存使用的解决方案:chunk

https://blackdeerdev.com/laravel-chunk-vs-cursor/

Chunk:它会为你的查询“分页”,这样你就可以使用更少的内存。

【讨论】:

【参考方案2】:

由于 Laravel Query Builder 使用stdObj 来表示它的结果,你会有很多开销:

每个对象都将存储行本身的值以及每列的名称。所以你的 32 个字节变成了很多字节。

【讨论】:

【参考方案3】:

在 PHP 中,每个变量都使用特定的数据结构进行处理,以允许 dynamic typinggarbage collection 等等。 你可以在这里看到一篇(相当老但还可以)的文章:link

您还可以看到数组有更具体的处理,因为它需要一个桶,例如存储被视为字符串的数组键。

所有这一切意味着(根据文章)大约 144 字节的数据用于存储数组的元素。 好吧,虽然我不能确切地解释你的结果,但我仍然可以告诉你,在你的情况下,有这样的事情:

300 000 * 144 * 4 = 172 800 000

这意味着 300000 行 4 个变量,其中 144 字节 由变量组成。

正如你所看到的,即使我的数学没有考虑到 PHP 7 的改进和其他因素,它也与你得到的结果相差不远......

【讨论】:

以上是关于为啥 PHP 使用大量内存来存储查询结果的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个 PHP MongoDB 查询即使有结果也不返回任何结果?

为啥这个 MySQL 存储函数给出的结果与在查询中进行计算不同?

为啥在 PostgreSQL 存储过程中查询没有“结果数据的目标”?

PHP MySQL Cursor 实现以及它们如何管理内存

查询存储过程的结果

将sql查询存储在列表中时PHP内存耗尽