Использование памяти запроса Doctrine

Доктрина, как представляется, занимает более 4 МБ ОЗУ для выполнения простого простого запроса:

print memory_get_peak_usage()." <br>\n";
$q = Doctrine_Query::create()
    ->from('Directories d')
    ->where('d.DIRECTORY_ID = ?', 5);

$dir = $q->fetchOne();
print $dir['name']." ".$dir['description']."<br>\n";

print memory_get_peak_usage()." <br>\n";

/***************  OUTPUT:  **************************

6393616
testname testdescription
10999648

/***************************************************/

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

Возможно, что-то не так с тем, как у меня установлена ​​система, или это стандартное использование памяти для Doctrine?

Ответ 1

Из того, что я вижу, ваш код не кажется неправильным...


В качестве теста я установил быстрый пример с очень простой таблицей (всего четыре поля).

Вот соответствующий код:

var_dump(number_format(memory_get_peak_usage()));

$test = Doctrine::getTable('Test')->find(1);

var_dump(number_format(memory_get_peak_usage()));

При этом у меня есть такой вывод:

string '1,316,088' (length=9)
string '2,148,760' (length=9)

Учитывая, что таблица действительно проста и что я только выбираю одну строку, она мне кажется слишком "большой", но это вполне согласуется с тем, что вы получаете, и тем, что я видел в других проектах: - (


Если вам нужно только отображать свои данные и не работать с ним (например, update/delete/...), решение может состоять в том, чтобы не извлекать сложные объекты, а только простой массив:

$test = Doctrine::getTable('Test')->find(1, Doctrine::HYDRATE_ARRAY);

Но в этом случае это не имеет большого значения, на самом деле:-(:

string '1,316,424' (length=9)
string '2,107,128' (length=9)

Только 40 КБ разницы - ну, с большими объектами и другими строками, это может быть хорошей идеей...


В руководстве Doctrine есть страница под названием Повышение производительности; возможно, это может вам помочь, особенно для этих разделов:


О, кстати, я сделал этот тест на PHP 5.3.0; возможно, это может повлиять на объем используемой памяти...

Ответ 2

Я согласен с ответом на romanb - использование кеша OpCode является определенным условием при использовании больших libs/frameworks.

Пример, связанный с кэшированием OpCode

Недавно я использовал использование Doctrine с Zend Framework, и мне было любопытно использовать память - так, как OP, я создал метод, использующий аналогичные критерии для теста OPs, и провел его как общий тест, чтобы увидеть, что пик ZF + Doctrine использование памяти будет.

Я получил следующие результаты:

Результат без APC:

10.25 megabytes
RV David
16.5 megabytes

Результат с APC:

3 megabytes
RV David
4.25 megabytes

Кэширование Opcode делает очень существенную разницу.

Ответ 3

Ну, откуда взялось это использование памяти? Как отметил Паскаль МАРТИН, гидратация массивов не имеет большого значения, что логично в отношении того, что мы говорим только о нескольких рекордах здесь.

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

Если у вас нет настройки APC, тогда да, что-то не так с настройкой вашей системы. Dont даже начать измерять производительность и ожидать хороших результатов с любой большой php-библиотекой без кэша операций операций, например APC. Это не только ускорит выполнение, но и уменьшит использование памяти не менее чем на 50% для всех загрузок страниц, кроме самого первого (где APC необходимо сначала кэшировать байткоды).

И 4MB с вашим простым примером действительно пахнет no-APC, иначе это будет действительно немного.

Ответ 4

Предостережение с помощью fetchOne() в Doctrine Query. Этот вызов функции не будет добавлять "Limit 1" в SQL

Если вам просто нужно получить одну запись из БД, убедитесь, что:

$q->limit(1)->fetchOne() 

Использование огромной памяти на большой таблице.

Вы можете видеть, что fetchOne() будет сначала извлекать из базы данных в виде коллекции, а затем возвращать первый элемент.

public function fetchOne($params = array(), $hydrationMode = null)
{
    $collection = $this->execute($params, $hydrationMode);

    if (is_scalar($collection)) {
        return $collection;
    }

    if (count($collection) === 0) {
        return false;
    }

    if ($collection instanceof Doctrine_Collection) {
        return $collection->getFirst();
    } else if (is_array($collection)) {
        return array_shift($collection);
    }

    return false;
}

Ответ 5

Doctrine предоставляет функцию free() в Doctrine_Record, Doctrine_Collection и Doctrine_Query, которая устраняет круговые ссылки на эти объекты, освобождая их для сбора мусора. Дополнительная информация..

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

  • $record- > free (true) - будет делать глубокие free-up'ы, free calls() для всех отношений.
  • $collection- > free() - это освободит все ссылки на коллекцию
  • Doctrine_Manager:: connection() → clean()/clear() - очистить соединение (и удалить записи идентификационной карты)
  • $query- > свободный()

Ответ 6

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

  • Какую версию Doctrine вы используете?
  • Вы используете автозагрузчик?

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

Ответ 7

Я только что сделал "daemonized" script с symfony 1.4 и установил следующее, остановив память:

sfConfig::set('sf_debug', false);