У меня есть следующий приблизительный код (полный код - 146 строк, 90 из которых являются синтаксическим анализом строк, при необходимости можно добавить):
ini_set('memory_limit', '7G');
$db = new PDO("mysql:host=".$dbhost.";dbname=".$dbname, $dbuser, $dbpass, array(PDO::ATTR_PERSISTENT => true));
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$db_ub = new PDO("mysql:host=".$dbhost.";dbname=".$dbname, $dbuser, $dbpass, array(PDO::ATTR_PERSISTENT => true));
$db_ub->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
$stmt = $db->prepare('select columns from stats where timestamp between ? and ?');
$stmt->execute(array('2020-04-25', '2020-05-25'));
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo memory_get_usage() .PHP_EOL;
echo $row['id'] . PHP_EOL;
$stmt2 = $db_ub->prepare('select somedata from users limit 1');
$stmt2->execute();
$row2 = $stmt2->fetch(PDO::FETCH_ASSOC);
$type = !empty($row2['somedate']) ? 5 : 4;
$result = $db_ub->prepare('insert ignore into newtable (old, type) values (?, ?)');
$result->execute(array($row['id'], $type));
}
во время $stmt->execute(array('2020-04-25', '2020-05-25'));
потребление моей памяти равно .34GB
(используя ps aux | grep 'php ' | awk '{$5=int(100 * $5/1024/1024)/100"GB";}{ print;}'
для отслеживания потребления во время select
и show full processlist
SQL, чтобы проверить). Как только сценарий попадает в while
, он увеличивается до +5 ГБ.
Тестирование setattribute
var_dump($db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false));
похоже, что это повлияло на:
bool(true)
, но поведение не меняется при переключении с буферизацией или без буферизации.
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false)
и
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true)
Использование echo $db->getAttribute(constant('PDO::MYSQL_ATTR_USE_BUFFERED_QUERY'));
также показывает изменения настроек.
Перемещение настройки в оператор, а не соединение как https://www.php.net/manual/en/ref.pdo-mysql.php предложено также не сработало.
$stmt = $db->prepare('select columns from stats where timestamp between ? and ?', array(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false));
Я также попытался переместить настройку буфера на соединение без каких-либо последствий:
$db = new PDO("mysql:host=".$dbhost.";dbname=".$dbname, $dbuser, $dbpass, array(PDO::ATTR_PERSISTENT => true, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false));
Отключение второго соединения похоже, позволяет небуферизованному запросу функционировать должным образом:
ini_set('memory_limit', '1G');
$db = new PDO("mysql:host=".$dbhost.";dbname=".$dbname, $dbuser, $dbpass, array(PDO::ATTR_PERSISTENT => true, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false));
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
//$db_ub = new PDO("mysql:host=".$dbhost.";dbname=".$dbname, $dbuser, $dbpass, array(PDO::ATTR_PERSISTENT => true));
//$db_ub->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
$stmt = $db->prepare('select columns from stats where timestamp between ? and ?');
$stmt->execute(array('2019-01-25', '2019-11-25'));
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo memory_get_usage() .PHP_EOL;
echo $row['id'] . PHP_EOL;
/*
$stmt2 = $db_ub->prepare('select somedata from users limit 1');
$stmt2->execute();
$row2 = $stmt2->fetch(PDO::FETCH_ASSOC);
$type = !empty($row2['somedate']) ? 5 : 4;
$result = $db_ub->prepare('insert ignore into newtable (old, type) values (?, ?)');
$result->execute(array($row['id'], $type));
*/
}
Это использование memory_get_usage
не превышает 379999
.
Если я раскомментирую второе соединение и сделаю его небуферизованным также я получаю:
Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
Второе буферизованное соединение выполняет, как первоначально описано, большое потребление памяти при выполнении. Если ini_set('memory_limit'
высокий, он работает, если низкий - ошибки. Использование большого memory_limit
не является возможным решением.
Использовалось (Red Hat Enterprise Linux Server release 7.3 (Maipo)
):
php71u-pdo.x86_64 7.1.19-1.ius.centos7
Сценарий перемещен на новую машину (Amazon Linux release 2 (Karoo)
):
php73-pdo.x86_64 7.3.17-1.el7.ius
и имеют такое же поведение.