Nano Hash - криптовалюты, майнинг, программирование

PDO::fetchAll против PDO::fetch в цикле

Просто быстрый вопрос.

Есть ли разница в производительности между использованием PDO::fetchAll() и PDO::fetch() в цикле (для больших наборов результатов)?

Я получаю доступ к объектам определяемого пользователем класса, если это имеет значение.

Мое первоначальное необдуманное предположение заключалось в том, что fetchAll может быть быстрее, потому что PDO может выполнять несколько операций в одном операторе, а mysql_query может выполнять только одну. Однако я мало знаю о внутренней работе PDO, и в документации ничего не говорится об этом, а также о том, является ли fetchAll() просто циклом на стороне PHP, сброшенным в массив.

Любая помощь?

05.05.2010

  • Я не знаю, но я подозреваю, что это будет тривиально для сравнения. 05.05.2010

Ответы:


1

Маленький бенчмарк с 200 тысячами случайных записей. Как и ожидалось, метод fetchAll работает быстрее, но требует больше памяти.

Result :
fetchAll : 0.35965991020203s, 100249408b
fetch : 0.39197015762329s, 440b

Используемый эталонный код:

<?php
// First benchmark : speed
$dbh = new PDO('mysql:dbname=testage;dbhost=localhost', 'root', '');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = 'SELECT * FROM test_table WHERE 1';
$stmt = $dbh->query($sql);
$data = array();
$start_all = microtime(true);
$data = $stmt->fetchAll();
$end_all = microtime(true);

$stmt = $dbh->query($sql);
$data = array();
$start_one = microtime(true);
while($data = $stmt->fetch()){}
$end_one = microtime(true);

// Second benchmark : memory usage
$stmt = $dbh->query($sql);
$data = array();
$memory_start_all = memory_get_usage();
$data = $stmt->fetchAll();
$memory_end_all = memory_get_usage();

$stmt = $dbh->query($sql);
$data = array();
$memory_end_one = 0;
$memory_start_one = memory_get_usage();
while($data = $stmt->fetch()){
  $memory_end_one = max($memory_end_one, memory_get_usage());
}

echo 'Result : <br/>
fetchAll : ' . ($end_all - $start_all) . 's, ' . ($memory_end_all - $memory_start_all) . 'b<br/>
fetch : ' . ($end_one - $start_one) . 's, ' . ($memory_end_one - $memory_start_one) . 'b<br/>';
05.05.2010
  • Да нет. Это цель теста: сначала вы выполняете fetchAll, а затем выполняете работу с данными. Во-вторых, вы должны получить одну строку, выполнить работу с этой строкой, а затем получить следующую строку. Хорошим примером может быть отображение таблицы данных: нужно ли хранить ВСЕ данные перед записью в буфер или нет? 05.01.2011
  • Извините за некропост, я не понимаю, почему люди говорят, что это плохой тест. Нет причин хранить весь набор данных, если только вы не вернете эти данные пользователю... что, во-первых, просто плохо, в этом случае используйте пейджинг. Если вам нужно массово изменить данные в базе данных, вы должны сделать это в базе данных либо с помощью сценария, либо с помощью хранимой процедуры, например. временные таблицы. 17.04.2014
  • -1. Это определенно плохой тест. Память не настоящая память. memory_get_usage(/* true */) показывает вам память, выделенную самим PHP. Он не показывает выделенную память LIBRARY. Libmysqlclient и mysqlnd используют свою собственную память. Не память PHP. 16.06.2015
  • Разве это не зависит от того, используете ли вы буферизованные запросы или нет @ бвоеби? Поскольку по умолчанию используется буферизация, я подумал, что это означает, что результаты запроса отправляются в процесс php, следовательно, используется его память? что касается памяти, возможно, бенчмарк должен использовать отдельные скрипты и memory_get_peak_usage(true) 22.07.2015
  • Только @FélixGagnon-Grenier memory_get*usage() покажет память, контролируемую PHP. Прямые вызовы malloc() или mmap() при этом не учитываются. И конечно, небуферизованные запросы в основном не читают из сокета. Это означает, что результаты затем буферизуются на стороне сервера mysql. Но буферизованные запросы хранятся на стороне клиента… в памяти libmysqlclient, которая выделяется через malloc(). (mysqlnd будет использовать emalloc(), который выделяет память с помощью распределителя памяти Zend) … Но этот тест, очевидно, был выполнен с помощью libmysqlclient. (Поскольку цифры слишком нереальны для mysqlnd.) 23.07.2015
  • Удивительно, как плохие комментарии, такие как +27, привлекают внимание к очень хорошо разработанному тесту. Я бы использовал истинное использование и использование внутренней памяти отдельно, но за исключением этого, это именно то, что я искал. pdo правильно обрабатывает fetch() и не буферизует все внутри, это то, что мне нужно было знать. 11.10.2017

  • 2

    Одна вещь о PHP, которую я обнаружил почти всегда, заключается в том, что функция, которую вы реализуете самостоятельно, почти всегда будет медленнее, чем эквивалент PHP. Это связано с тем, что когда что-то реализовано на PHP, у него нет всех оптимизаций времени компиляции, которые есть в C (на котором написан PHP), и есть большие накладные расходы на вызовы функций PHP.

    05.05.2010
  • Бывают случаи, когда стоит не использовать встроенный PHP. Например, поиск в отсортированном массиве (бинарный поиск ftw). 05.05.2010
  • Я не уверен, что полностью понимаю ваш ответ, но мне нужно снова выполнить пару операций со всеми объектами после их извлечения, что, несомненно, потребует еще одного цикла foreach. Должен ли я просто извлекать один объект за раз и выполнять операции над каждым объектом по мере его извлечения? 05.05.2010
  • @AlReece45 AlReece45 Вы описали две совершенно разные функции. Я говорил о повторной реализации функции сортировки в PHP по сравнению с использованием PHP sort. @Byron Бьюсь об заклад, вы обнаружите, что получение всех результатов с помощью fetchAll () по-прежнему будет быстрее, но вы можете сравнить его с microtime(TRUE), если у вас есть сомнения. 05.05.2010
  • @Reece45 Двоичный поиск логарифмический; он должен быть быстрым, написан ли он на C или PHP. Сортировка OTOH – это O(n log n) -- здесь можно немного сэкономить. 05.06.2013
  • На теоретическом уровне может иметь смысл реализовать бинарный поиск в PHP, но если ваше N не очень велико (что в большинстве случаев неверно, если только вы не делаете что-то не так), лучше просто использовать O(N), который PHP обеспечивает. 05.06.2013

  • 3

    все тесты, выше которых измеряется «занимаемая память», на самом деле неверны по очень простой причине.

    PDO по умолчанию загружает все в память, и ему все равно, используете ли вы fetch или fetchAll. Чтобы действительно получить преимущества от небуферизованных запросов, вы должны указать PDO использовать небуферизованные запросы:

    $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

    В этом случае вы увидите огромную разницу в объеме памяти скрипта.

    01.03.2017
  • В чем разница между использованием $stmt->fetch() при использовании буферизованных запросов (по умолчанию) и использованием $stmt->fetch() с небуферизованными запросами (атрибут PDO::MYSQL_ATTR_USE_BUFFERED_QUERY имеет значение false)? Я видел, что даже если вы используете режим буферизации по умолчанию, $stmt->fetch() работает с очень большими наборами данных, а $stmt->fetchAll() может возвращать ошибку ограничения памяти. Так $stmt->fetch() вроде как unbuffered? 24.01.2018

  • 4

    @Арх

    // $data in this case is an array of rows;
    
    $data = $stmt->fetchAll();
    
    
    // $data in this case is just one row after each loop;
    
    while($data = $stmt->fetch()){}
    
    
    // Try using
    
    $i = 0;
    
    while($data[$i++] = $stmt->fetch()){}
    

    Разница в памяти должна стать незначительной

    03.06.2010
  • @stancu верхний и нижний варианты фактически идентичны, а дополнительный MEM, наблюдаемый при использовании fetch(), вероятно, является артефактом накладных расходов while(). Смысл fetch() состоит в том, чтобы обрабатывать строку за раз, используя while() для выполнения того же действия, что и fetchAll(PDO::FETCH_NUM) глупо, поскольку вы теряете оптимизацию компилятора C-уровня, происходящую в PDO. модуль. 14.05.2014

  • 5

    Как сказал Михай Станку, разницы в памяти почти нет, хотя fetchAll лучше, чем fetch + while.

    Result : 
    fetchAll : 0.160676956177s, 118539304b
    fetch : 0.121752023697s, 118544392b
    

    Я получил приведенные выше результаты при правильном запуске:

    $i = 0;
    while($data[$i++] = $stmt->fetch()){
        //
    }
    

    Таким образом, fetchAll потребляет меньше памяти, но fetch + while работает быстрее! :)

    24.12.2010
  • Быстрее? 0,16 (fetchAll) против 0,12 (fetch) 25.12.2010
  • При значительно больших наборах результатов вы увидите значительную разницу между PDOStatement::fetch() и PDOStatement::fetchALL(). Определение того, что считается значительно большим, будет зависеть от размера каждой строки. Кроме того, по умолчанию PDOStatement::Fetch()/fetchAll() использует режим выборки PDO::FETCH_BOTH, который фактически удваивает размер каждой строки, изменение этого параметра может помочь уменьшить использование MEM в больших наборах результатов. 14.05.2014
  • Кроме того, голосование против было за то, что вы не предоставили никакой справочной статистики для вашего теста, что сделало его изначально ошибочным. Различные части накладных расходов с функциями PHP и то, что не объясняет разницу в MEM. 14.05.2014

  • 6

    Но наверняка, если вы храните извлеченные данные в массиве, использование памяти будет одинаковым?

    <?php
    define('DB_HOST', 'localhost');
    define('DB_USER', 'root');
    define('DB_PASS', '');
    // database to use
    define('DB', 'test');
    try
    {
       $dbh = new \PDO('mysql:dbname='. DB .';host='. DB_HOST, DB_USER, DB_PASS);   $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
       $sql = 'SELECT * FROM users WHERE 1';
       $stmt = $dbh->query($sql);
       $data = array();
       $start_all = microtime(true);
       $data = $stmt->fetchAll();
       $end_all = microtime(true);
    
       $stmt = $dbh->query($sql);
       $data = array();
       $start_one = microtime(true);
       while($data = $stmt->fetch()){}
       $end_one = microtime(true);
    
       // Second benchmark : memory usage
       $stmt = $dbh->query($sql);
       $data = array();
       $memory_start_all = memory_get_usage();
       $data = $stmt->fetchAll();
       $memory_end_all = memory_get_usage();
    
       $stmt = $dbh->query($sql);
       $data = array();
       $memory_end_one = 0;
       $memory_start_one = memory_get_usage();
       while($data[] = $stmt->fetch()){
         $memory_end_one = max($memory_end_one, memory_get_usage());
       }
    
       echo 'Result : <br/>
       fetchAll : ' . ($end_all - $start_all) . 's, ' . ($memory_end_all - $memory_start_all) . 'b<br/>
       fetch : ' . ($end_one - $start_one) . 's, ' . ($memory_end_one - $memory_start_one) . 'b<br/>';
    }
    catch ( PDOException $e )
    {
       echo $e->getMessage();
    }
    ?>
    
    Result : 
    fetchAll : 2.6941299438477E-5s, 9824b
    fetch : 1.5974044799805E-5s, 9824b
    
    28.05.2012

    7

    Я знаю, что это старая тема, но я столкнулся с тем же вопросом. Запустив свой нехитрый "бенчмарк" и прочитав, что тут писали другие, я пришел к выводу, что это не точная наука и при том, что нужно стремиться писать качественный, легкий код, нет смысла тратить слишком много времени на старте проекта.

    Мое предложение: соберите данные, запустив код (в бета-версии?) на некоторое время, а затем начните оптимизацию.

    В моем простом тесте (только проверенное время выполнения) у меня есть результаты, варьирующиеся от 5% до 50% ОБА. Я запускаю обе опции в одном и том же скрипте, но когда я запускаю fetch + while first, это происходит быстрее, чем fetchall, и наоборот. (Я знаю, что должен был запустить их один раз и пару сотен раз, получить медиану и среднее значение, а затем сравнить, но, как я уже сказал в начале, я пришел к выводу, что в моем случае еще слишком рано начинать это делать.)

    02.05.2012
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..