2010-05-05 5 views
59

Просто быстрый вопрос.PDO :: fetchAll vs. PDO :: выборка в петле

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

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

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

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

+0

я не знаю, но Я подозреваю, что было бы тривиально ориентироваться. – Timothy

ответ

67

Маленький бенчмарк с 200k случайных записей. Как и ожидалось, метод 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/>'; 
+28

Ваш тест полностью неисправен! Вы не храните свои данные в массиве $ data во втором эталоне (в 'while'). Вы действительно считали, что разница в памяти - это БОЛЬШОЙ ??? Ничего себе это ДЕЙСТВИТЕЛЬНО ПЛОХОЙ ОТВЕТ **! – Rudie

+60

Да, нет. Это цель теста: во-первых, вы делаете fetchAll THEN, выполняйте работу над данными. Во-вторых, вы получите одну строку, выполните работу над этой строкой, а затем извлечете следующую строку. Хорошим примером может быть отображение таблицы данных, нужно ли хранить ВСЕ данные перед записью в буфер или нет? – Arkh

+1

Извините за некрополя, я не понимаю, почему люди говорят, что это плохой бенчмарк. Нет оснований хранить весь набор данных, если вы не возвращаете данные пользователю ... что просто плохо в первую очередь, используйте пейджинг в этом случае. Если вам необходимо модифицировать данные в базе данных в массовом порядке, вы должны сделать это в базе данных либо со сценарием, либо с помощью хранимой процедуры, например. временные таблицы. – Populus

9

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

+0

Бывают случаи, когда стоит использовать PHP-встроенный. Например, поиск отсортированного массива (бинарный поиск ftw). – Reece45

+2

Я не уверен, что я полностью понимаю ваш ответ, но после того, как они были извлечены, мне придется выполнить пару операций над всеми объектами, что, несомненно, потребует другой цикл foreach. Должен ли я просто придерживаться выборки по одному объекту за раз и выполнять операции над каждым объектом по мере его извлечения? –

+0

@ AlReece45 Вы описали две совершенно разные функции. Я говорил о переопределении функции сортировки в PHP и использовании 'sort' в PHP. @Byron Я уверен, вы обнаружите, что получение всех результатов с помощью fetchAll() будет по-прежнему быстрее, но вы можете сравнить его с «microtime (TRUE)», если у вас есть сомнения. –

8

@Arkh

// $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()){} 

Разница памяти должна стать neglijable

+2

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

4

Как Михай Stancu говорил, что нет почти никакой разницы памяти хотя fetchAll бьет fetch + while.

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

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

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

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

+6

Быстрее? 0.16 ('fetchAll') против 0.12 (' fetch') – Joost

+3

К сожалению, я устал .. Отредактировано. :} – Rihards

+1

Со значительно большими наборами результатов вы увидите существенную разницу между PDOStatement :: fetch() и PDOStatement :: fetchALL(). Определение того, что квалифицируется как «значительно большее», зависит от размера каждой строки. Кроме того, по умолчанию PDOStatement :: Fetch()/fetchAll() использует режим выборки PDO :: FETCH_BOTH, который эффективно удваивает размер каждой строки, изменение этого может помочь смягчить использование MEM на больших результирующих наборах. – DavidScherer

1

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

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

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

3

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

<?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 
4

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

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

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

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

+0

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

Смежные вопросы