Подтвердить что ты не робот

Производительность mysqli_fetch_assoc() PHP5.4 по сравнению с PHP7.0

У меня большой запрос MySQL (строки 1.8M, 25 столбцов), и мне нужно сделать из него 2-мерный массив (таблица памяти на основе первичного ключа).

Код работает так, как ожидалось, но создание $table занимает много времени в PHP7.0.

В чем причина того, что PHP7.0 работает намного хуже? Мой основной интерес в mysqli.

Благодарим вас за любые идеи - PHP7 сохранит мне много памяти, если я могу исправить производительность.

фрагмент кода mysqli

$start = microtime(true);

$vysledek = cluster::query("SELECT * FROM `table` WHERE 1");
$query_time = (microtime(true) - $start);
$start_fetch = microtime(true);
while($zaznam = mysqli_fetch_assoc ( $vysledek )){
  $fetch_time+= (microtime(true) - $start_fetch);
  $start_assign = microtime(true);
  $table[$zaznam['prikey']] = $zaznam;
  $assign_time+= (microtime(true) - $start_assign);
  $start_fetch = microtime(true);
}
$total_time+= (microtime(true) - $start);
echo round($assign_time, 2).' seconds to set the array values\n';
echo round($query_time, 2).' seconds to execute the query\n';
echo round($fetch_time, 2).' seconds to fetch data\n';
echo round($total_time, 2).' seconds to execute whole script\n';
echo "Peak Memory Usage:".round(memory_get_peak_usage(true)/(1024 * 1024), 2)." MB\n";

результаты mysqli

Deb 7 PHP 5.4 mysqlnd 5.0.10
1,8 секунды для установки значений массива
8,37 секунд для выполнения запроса
13.49 секунд для получения данных
24.42 секунды для выполнения целых script
Использование пиковой памяти: 8426,75 МБ

Deb 8 PHP 5.6 mysqlnd 5.0.11-dev
1,7 секунды для установки значений массива
8.58 секунд для выполнения запроса
12,55 секунды для получения данных
23,6 секунды для выполнения целых script
Использование пиковой памяти: 8426,75 МБ

Deb 8 PHP 7.0 mysqlnd 5.0.12-dev
0,73 секунды для установки значений массива
8,63 секунды для выполнения запроса
126,71 секунды для получения данных
136.46 секунд для выполнения целых script

Использование пиковой памяти: 7394,27 МБ

Deb 8 PHP 7.0 mysqlnd 5.0.12-dev расширенный бенчмаркинг

У меня расширенный бенчмаркинг для выборки раздела, чтобы сообщать каждые 100 тыс. строк со следующими результатами:

Линии выбрали 100000 в 1.87s
Линии выбрали 300000 в 5.24s
Линии выбрали 500000 в 10.97s
Линии выбрали 700000 в 19.17s
Линии выбрали 900000 в 29,96s
Линии выбрали 1100000 в 43.03s
Линии выбрали 1300000 в 58.48s
Линии выбрали 1500000 в 76,47s
Линии выбрали 1700000 в 96,73s
Линии выбрали 1800000 в 107.78s

DEB8 PHP7.1.0-dev libclient 5.5.50

1,56 секунды для установки значений массива
8,38 секунды для выполнения запроса
456,52 секунды для получения данных
467,68 секунд для выполнения целых script

Использование пиковой памяти: 8916 МБ

DEB8 PHP7.1.0-dev libclient 5.5.50 расширенный бенчмаркинг

Линии выбрали 100000 в 2.72s
Линии выбрали 300000 в 15,7 с
Линии выбрали 500000 в 38,7 с
Линии выбрали 700000 в 71,69s
Линии выбрали 900000 в 114,8 с
Линии выбрали 1100000 в 168.18s
Линии выбрали 1300000 в 231,69 секунд
Линии выбрали 1500000 в 305,36s
Линии выбрали 1700000 в 389.05s
Линии выбрали 1800000 в 434,71 с

DEB8 PHP7.1.0-dev mysqlnd 5.0.12-dev

1,51 секунды для установки значений массива
9,16 секунды для выполнения запроса
261,72 секунды для получения данных
273,61 секунды для выполнения целых script

Использование пиковой памяти: 8984,27 МБ

DEB8 PHP7.1.0-dev mysqlnd 5.0.12-dev расширенный бенчмаркинг

Линии выбрали 100000 в 3.3s
Линии выбрали 300000 в 13,63s
Линии выбрали 500000 в 29.02s
Линии выбрали 700000 в 49,21s
Линии выбрали 900000 в 74.56s
Линии выбрали 1100000 в 104.97s
Линии выбрали 1300000 в 140.03s
Линии выбрали 1500000 в 180.42s
Линии выбрали 1700000 в 225.72s
Линии, заказанные 1800000 в 250.01s

фрагмент кода PDO

$start = microtime(true);
$sql = "SELECT * FROM `table` WHERE 1";
$vysledek = $dbh->query($sql, PDO::FETCH_ASSOC);
$query_time = (microtime(true) - $start);
$start_fetch = microtime(true);
foreach($vysledek as $zaznam){
  $fetch_time+= (microtime(true) - $start_fetch);
  $start_assign = microtime(true);
  $table[$zaznam['prikey']] = $zaznam;
  $assign_time+= (microtime(true) - $start_assign);
  $start_fetch = microtime(true);
}
$total_time+= (microtime(true) - $start);
echo round($assign_time, 2).' seconds to set the array values\n';
echo round($query_time, 2).' seconds to execute the query\n';
echo round($fetch_time, 2).' seconds to fetch data\n';
echo round($total_time, 2).' seconds to execute whole script\n';
echo "Peak Memory Usage:".round(memory_get_peak_usage(true)/(1024 * 1024), 2)." MB\n";

Результаты PDO

Deb 7 PHP 5.4 mysqlnd 5.0.10
1,85 секунды для установки значений массива
12,51 секунды для выполнения запроса
16,75 секунд для получения данных
31,82 секунды для выполнения целых script
Использование пиковой памяти: 11417.5 МБ

Deb 8 PHP 5.6 mysqlnd 5.0.11-dev
1,75 секунды для установки значений массива
12,16 секунды для выполнения запроса
15,72 секунды для получения данных
30.39 секунд для выполнения целых script
Использование пиковой памяти: 11417.75 МБ

Deb 8 PHP 7.0 mysqlnd 5.0.12-dev
0,71 секунды для установки значений массива
35,93 секунды для выполнения запроса
114,16 секунды для получения данных
151.19 секунд для выполнения целых script

Использование пиковой памяти: 6620.29 МБ

Сравнительный код базовой линии

 $start_query = microtime(true);
 exec("mysql --user=foo --host=1.2.3.4 --password=bar -e'SELECT * FROM `profile`.`table`' > /tmp/out.csv");
 $query_time = (microtime(true) - $start_query);
 echo round($query_time, 2).' seconds to execute the query \n';

Время выполнения аналогично для всех систем в 19 секунд + 1 секунда.

Основываясь на вышеприведенных наблюдениях, я бы сказал, что PHP 5.X разумен, поскольку выполняется немного больше работы, чем просто сбрасывание в файл.

  • все 3 сервера находятся на одном хосте (исходный и оба тестовых сервера).
  • тесты повторяются при повторении
  • в памяти уже есть аналогичная переменная, мне нужно сделать это для сравнения удалено для тестирования, не связано с проблемой
  • Процессор на 100% все время
  • Оба сервера имеют 32G RAM и swappiness, установленный в 1, целью является выполнение его как операции с памятью.
  • тестовый сервер посвящен, больше ничего не работает
  • php.ini изменен между основными версиями, но все параметры, относящиеся к mysqli/PDO, кажутся одинаковыми
  • Машина Deb8 была понижена до версии PHP5.6, и проблема исчезла после переустановки PHP7 назад

  • Сообщается об ошибке на php.net - ID 72736, так как я считаю, что было доказано, что проблема заключается в PHP, а не в системе или какой-либо другой конфигурации

Изменить 1: добавлено сравнение PDO

Изменить 2: добавлены маркеры эталонных тестов, отредактированы результаты PDO, так как была ошибка сравнения

Изменить 3: Основная очистка в исходном вопросе, перестроение кода снайперов для лучшего указания ошибки

Изменить 4: добавлен пункт о обновлении и обновлении PHP

Изменить 5: добавлен расширенный бенчмаркинг для DEB8 PHP7.0

Изменить 6: включен php7 config

Изменить 7: измерение производительности для PHP 7.1 dev с обеими библиотеками, скомпилированными с конфигурациями из епископа, удалило мою php-config

Изменить 8: добавлено сравнение с командой CLI, незначительные очистки

4b9b3361

Ответ 1

Для перекрестной ссылки: с выпуском PHP 7.1 1 декабря 2016 года эта проблема должна быть разрешена (в PHP 7.1).

PHP 7.0: даже в билете написано, что PHP-7.0 был исправлен, я еще не видел в последнем журнале изменений (7.0.13 от 10 ноября 2016 г., начиная с даты установки патча), что это часть текущей версии PHP 7.0.x. Возможно, со следующей версией.

Ошибка отслеживается вверх (благодаря отчету OP): Ошибка # 72736 - Медленная производительность при наборе большого набора данных с помощью mysqli/PDO ( bugs.php.net, август 2016 г.).

Ответ 2

Как представляется, проблема заключается в выборке (а не в создании массива), и мы знаем, что драйвер работает mysqlnd (который является библиотекой драйверов, независимо написанной командой PHP, не предоставленной MySQL AB aka Oracle), то перекомпиляция PHP с использованием libmysqlclient (который представляет собой MySQL AB, а также интерфейс Oracle) может улучшить ситуацию (или, по крайней мере, сократить пространство проблем).

В первую очередь я бы посоветовал написать небольшой script, который можно запустить из CLI, который демонстрирует проблему. Это поможет устранить любые другие переменные (модули веб-сервера, opcache и т.д.).

Затем я предлагаю перестроить PHP с помощью libmysqlclient, чтобы увидеть, улучшена ли производительность. Краткое руководство по перестройке PHP (для технически компетентного):

  • Загрузите исходный код нужной вам версии PHP
  • Декомпрессия и переход в каталог кода PHP
  • Запустить ./buildconf
  • Запустить ./configure --prefix=/usr --with-config-file-path=/etc/php5/apache2 --with-config-file-scan-dir=/etc/php5/apache2/conf.d --build=x86_64-linux-gnu --host=x86_64-linux-gnu --sysconfdir=/etc --localstatedir=/var --mandir=/usr/share/man --enable-debug --disable-static --with-pic --with-layout=GNU --with-pear=/usr/share/php --with-libxml-dir=/usr --with-mysql-sock=/var/run/mysqld/mysqld.sock --enable-dtrace --without-mm --with-mysql=shared,/usr --with-mysqli=shared,/usr/bin/mysql_config --enable-pdo=shared --without-pdo-dblib --with-pdo-mysql=shared,/usr CFLAGS="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -O2 -Wall -fsigned-char -fno-strict-aliasing -g" LDFLAGS="-Wl,-z,relro" CPPFLAGS="-D_FORTIFY_SOURCE=2" CXXFLAGS="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security"
  • Выполнить make && make test
  • Уходите.
  • Запустите sapi/cli/php -i и подтвердите версию и наличие libmysqlclient

Повторите тест. Лучше?