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

Why is PHP using a lot of memory to store a query result

我使用 Laravel 8 直接使用 query builderMySQL 8 table 执行查询以避免 Eloquent 开销,但我得到了无论如何都会消耗大量内存。
为了向您展示一个示例,我对 select 恰好 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();

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

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

Chunk: It will “paginate” your query, this way you use less memory.

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

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

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

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

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

300 000 * 144 * 4 = 172 800 000

这意味着 300000 行4 个变量144 字节 变量。

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