具有较大结果集的 PDO/MySQL 内存消耗

2022-08-30 16:55:37

我遇到了一个奇怪的时间来处理从大约30,000行的表中进行选择。

似乎我的脚本正在使用大量内存来表示查询结果的简单,仅向前走动。

请注意,此示例是一个有点人为的,绝对的最小示例,它与实际代码几乎没有相似之处,并且不能用简单的数据库聚合替换。它旨在说明一点,即每次迭代时都不需要保留每一行。

<?php
$pdo = new PDO('mysql:host=127.0.0.1', 'foo', 'bar', array(
    PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,
));
$stmt = $pdo->prepare('SELECT * FROM round');
$stmt->execute();

function do_stuff($row) {}

$c = 0;
while ($row = $stmt->fetch()) {
    // do something with the object that doesn't involve keeping 
    // it around and can't be done in SQL
    do_stuff($row);
    $row = null;
    ++$c;
}

var_dump($c);
var_dump(memory_get_usage());
var_dump(memory_get_peak_usage());

此输出:

int(39508)
int(43005064)
int(43018120)

我不明白为什么在任何时候几乎不需要保存任何数据的情况下使用40兆兆的内存。我已经计算出,通过将“SELECT *”替换为“SELECT home,away”,我可以将内存减少约6倍,但是我认为即使这种用法也非常高,桌子只会变得更大。

是否有我缺少的设置,或者 PDO 中是否有一些我应该注意的限制?如果它不能支持这一点,我很高兴摆脱PDO而支持mysqli,所以如果这是我唯一的选择,我如何使用mysqli来执行这个?


答案 1

创建连接后,需要将 PDO::MYSQL_ATTR_USE_BUFFERED_QUERY 设置为 false:

<?php
$pdo = new PDO('mysql:host=127.0.0.1', 'foo', 'bar', array(
    PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,
));
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

// snip

var_dump(memory_get_usage());
var_dump(memory_get_peak_usage());

此输出:

int(39508)
int(653920)
int(668136)

无论结果大小如何,内存使用量几乎保持不变。


答案 2

另一种选择是做这样的事情:

$i = $c = 0;
$query = 'SELECT home, away FROM round LIMIT 2048 OFFSET %u;';

while ($c += count($rows = codeThatFetches(sprintf($query, $i++ * 2048))) > 0)
{
    foreach ($rows as $row)
    {
        do_stuff($row);
    }
}

推荐