Рекомендация CakePHP для итерации огромной таблицы и создания карты сайта? - PullRequest
6 голосов
/ 03 марта 2010

Я пытаюсь создать карту сайта XML, используя CakePHP, из таблицы, которая на данный момент содержит более 50 000 записей, каждая запись эквивалентна URI в карте сайта. Теперь проблема, с которой я сталкиваюсь, заключается в том, что CakePHP запускает мне нехватку памяти при генерации по двум причинам:

  1. A find('all') создает огромный ассоциативный массив из всего 50 000 URI.
  2. Поскольку я не хочу выводить HTML из самого контроллера, я передаю ассоциативный массив, содержащий URI, приоритет, частоту изменения и т. Д., В представление с помощью вызова $this->set() - что опять-таки огромно, содержащий 50 000 индексов.

Можно ли вообще сделать это, следуя рекомендациям MVC и CakePHP?

Ответы [ 6 ]

4 голосов
/ 29 февраля 2012

Я знаю, что этот вопрос старый, но для действительно больших запросов я все еще не нашел хорошего решения.

Для перебора огромного набора результатов вы можете использовать методы DboSource.

Сначала получите DBO

$dbo = $this->Model->getDataSource();

Построить запрос

$sql = $dbo->buildStatement($options);

Затем выполните инструкцию и выполните итерацию результатов

if ($dbo->execute($sql))
{
    while ($dbo->hasResult() && $row = $dbo->fetchResult()) {
        // $row is an array with same structure like find('first')
    }
}
3 голосов
/ 03 марта 2010

У меня была похожая проблема на этой неделе, и я наткнулся на Containable Поведение. Это позволяет сократить любые связанные с отношениями запросы (если они у вас есть).

Лучшим решением было бы программно использовать LIMIT и OFFSET и циклически перебирать небольшие порции записей за раз. Это избавляет вас от одновременной вставки 50К записей в память.

2 голосов
/ 03 марта 2010

Вы уверены, что вам не хватает памяти на 50.000 записей? Даже если размер строки составляет 1 КБ (довольно большой), вам придется иметь дело с ~ 50 МБ данных? У моего P1 было достаточно оперативной памяти, чтобы справиться с этим. Установите значение memory_limit в php.ini выше значения по умолчанию. (Рассмотрим также настройку max_execution_time.)

С другой стороны, если вы считаете набор данных слишком большим и обрабатываете его как слишком ресурсоемкий, вам не следует динамически обслуживать эту страницу, это идеальная приманка DDoS. (По крайней мере, я бы сильно кешировал это.) Вы можете запланировать задание cron на повторную генерацию страницы каждые X часов с помощью сценария на стороне сервера без штрафов MVC за предоставление всех данных сразу для представления, это может работать с строками последовательно.

2 голосов
/ 03 марта 2010

find ('all') слишком жадный, вам нужно быть более конкретным, если вы не хотите исчерпывать память.

Как указано выше, используйте поведение Containable. Если вам нужны только результаты из вашей таблицы (без связанных таблиц) и только для пары полей, более точный запрос, подобный этому, должен быть лучше:

$results = $this->YourModel->find('all', array(
    'contain' => false,
    'fields' => array('YourModel.name', 'YourModel.url')
);

Вам также следует подумать о добавлении механизма кэширования html (cakePHP имеет встроенную функцию или использует механизм , предложенный Мэттом Карри ).

Конечно, это будет кэшированная версия, и она не будет полностью соответствовать вашему списку. Если вы хотите больше контроля, вы всегда можете сохранить результат в кеше тортов (используя Cache :: write ), используя обратные вызовы afterSave / afterDelete вашей модели, чтобы обновить кэшированное значение и воссоздать кэшированный xml-файл отсюда. .

1 голос
/ 30 июня 2012

Используйте https://github.com/jamiemill/cakephp_find_batch или реализуйте эту логику самостоятельно.

1 голос
/ 03 марта 2010

Вы пробовали unBindModel (если у вас есть отношения) ...

Всякий раз, когда мне приходится выполнять огромные запросы в cakephp, я просто использую «обычные» mysql-функции, такие как mysql_query, mysql_fetch_assoc и т. Д. Гораздо быстрее, и не хватает памяти ...

...