2016-07-27 2 views
19

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

Код работает, как ожидалось, но создание $ 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 секунд, чтобы установить массив значений
,37 секунд, чтобы выполнить запрос
13.49 секунд для извлечения данных
24,42 секунд, чтобы выполнить весь сценарий
пиковое использование памяти: 8426,75 MB

Deb 8 PHP 5.6 mysqlnd 5.0.11-DEV
1,7 секунд, чтобы установить массив значений
,58 секунд, чтобы выполнить запрос
12,55 секунд, чтобы получить данные
23,6 секунд, чтобы выполнить весь сценарий
пикового использования памяти: 8426,75 MB

Деб 8 PHP 7.0 mysqlnd 5.0.12-DEV
0,73 секунд, чтобы установить массив значений
,63 секунд, чтобы выполнить запрос
126,71 секунд, чтобы получить данные
136.46 секунд, чтобы выполнить весь сценарий

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

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

я продлил бенчмаркинга п или секция выборки, чтобы сообщить каждую 100k линии со следующими результатами:

линия принесла 100000 в 1.87s
линии принесла 300000 в 5.24s
линии принесла в 10.97s 500000
линий принесла 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 секунд, чтобы установить массив значений
,38 секунд, чтобы выполнить запрос
456,52 секунд, чтобы получить данные
467.68 секунд, чтобы выполнить весь сценарий

Пик Использование памяти: 8916 MB

DEB8 PHP7.1.0-DEV libclient 5.5.5 0 расширенного бенчмаркинга

Линия принесла 100000 в 2.72s
Lines принесло 300000 в 15.7s
линий принесла в 38.7s 500000
линий принесла 700000 в 71.69s
линий принесла 900000 в 114.8s
Линии ушей 1100000 в 168.18s
линии принес 1300000 в 231.69s
линии принес 1500000 в 305.36s
линии принес 1700000 в 389.05s
Линии принесла 1800000 в 434.71s

DEB8 PHP7.1.0-DEV mysqlnd 5.0.12-DEV

1,51 секунд, чтобы установить массив значений
9,16 секунд, чтобы выполнить запрос
261.72 секунд выборки данных
273.61 секунд, чтобы выполнить весь сценарий
Использование
Пик памяти: 8984,27 MB

DEB8 PHP7.1.0-DEV mysqlnd 5.0.12-DEV расширенного бенчмаркинг

Линия принесла 100000 в 3.3s
Lines принесло 300000 в 13.63s
линий принесла в 29.02s 500000
линий принесло 700000 в 49.21s
линии принесла 900000 в 74.56s
линий принесли 1100000 в 104.97s
линий принесли 1300000 в 140.03s
линий принесли 1500000 в 180.42s
линий принесли 1700000 в 225.72s
Линии 1800000 принес в 250.01s

ПДО фрагмент кода

$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"; 

ПДО Результаты

Deb 7 PHP 5.4 mysqlnd 5.0.10
1,85 секунды, чтобы установить массив значения
12.51 секунд, чтобы выполнить запрос
16,75 секунд, чтобы получить данные
31,82 секунд, чтобы выполнить весь сценарий
пиковое использование памяти: 11417,5 MB

Deb 8 PHP 5.6 mysqlnd 5.0.11-DEV
1,75 секунды задать массив значения
12,16 секунд, чтобы выполнить запрос
15,72 секунд, чтобы получить данные
30,39 секунд, чтобы выполнить весь сценарий
пикового использования памяти: 11417.75 MB

Деб 8 PHP 7.0 mysqlnd 5.0.12-DEV
0,71 секунд, чтобы установить массив значений
35,93 секунд, чтобы выполнить запрос
114,16 секунд, чтобы получить данные
151.19 секунд, чтобы выполнить весь скрипт

Пик использования памяти: 6620,29 MB

Базовый код сравнения

$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.сеть - ID 72736, так как я верю, что это было доказано, что проблема заключается в PHP, а не в системе или любой другой конфигурации

Edit 1: Добавлено PDO Сравнение

Edit 2: Добавлено бенчмаркинг маркеров, отредактированные результаты PDO, как были бенчмаркинга ошибкой

Edit 3: Основная очистка в оригинальном вопросе, перестроение кода snipets для лучшего индикации ошибки

Edit 4: добавлен пункт о DownGrade и модернизации PHP

Edit 5: добавлена ​​расширенная бенчмаркинг для DEB8 PHP7.0

Edit 6: включен php7 конфигурации

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

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

+0

Просто любопытно: вы пробовали то же самое с PDO? –

+0

Чтобы быть понятным, вы загружаете содержимое всего стола и сохраняете его в большом массиве жира? И сохранение второго массива аналогичного размера в памяти? Это, безусловно, не может быть самым эффективным способом сделать это. – Simba

+1

Фактически это чрезвычайно эффективно, в PHP5.4 вы можете сравнить 2 таблицы, как это, из двух разных БД за 40-60 секунд. В то же время операция не блокирует базы данных, вы можете делать отдельные вставки/удаления/обновления, и оригинальное приложение может работать над этими таблицами все время. Еще один момент заключается в том, что вы можете сделать это на разных серверах, так что, если вы исчерпаете память, вы не уничтожаете целевую базу данных. – Zmrzka

ответ

4

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

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

Ошибка отслежена вверх по течению (благодаря отчету OP): Bug #72736 - Slow performance when fetching large dataset with mysqli/PDO (bugs.php.net; Aug 2016).

1

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

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

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

  1. Скачать исходный код для PHP версии вы хотите
  2. распаковывать и перейти в каталог PHP кода
  3. Run ./buildconf
  4. Run ./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"
  5. Пробег make && make test
  6. Прогулка
  7. Пробег sapi/cli/php -i и confi rm версия и наличие libmysqlclient

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

+0

Исходное сообщение обновлено, в вашем руководстве по сборке было немало шагов, но это сработало в конце. Я верю, что было доказано, что проблема заключается в функции warqli_fetch_assoc() warper, а не в драйвере mysql, libmysqlclient выполняет значительно хуже, и раньше он работал намного быстрее. Убрал старые комментарии, так как они не связаны. – Zmrzka

+0

Для чего он стоит, mysqlnd был совместно разработан Ульфом Венделем, инженером, который работал в MySQL AB и теперь работает для Oracle. http://blog.ulf-wendel.de/2007/php-mysqlnd-to-support-pdo-mysqlnd-in-php-53/ –

+0

Итак, теперь для базовой линии, если вы выполняете одну и ту же выборку с помощью командной строки 'mysql', сколько времени потребуется? – bishop