Оптимизация выборки при недостатке памяти в HostCMS

Работая с большими выборками в HostCMS можно столкнуться с проблемой недостатка памяти, которая будет проявляться, например, вот такой ошибкой:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in /modules/core/orm.php on line 730

Техническая поддержка HostCMS помогла нам с оптимизацией кода и решением этой проблемы. 

Так, в нашем модуле SEO-фильтр мы столкнулись с проблемой нехватки памяти при генерации sitemap. У модуля есть генератор URL-ов*, который создает URL-ы для разных возможных комбинаций параметров(дополнительных свойств) в фильтре в клиентской части. И таких комбинаций может получится огромное множество. В одном из проектов их более 100 000. Но в данном случае у нас было всего 20 000 URL-ов и этого уже хватало, чтобы вызвать ошибку нехватки памяти. Все эти 20 000 получались одним запросом:

$oKadseofilters = Core_Entity::factory('kadseofilter');
$oKadseofilters->queryBuilder()
 ->join('shops', 'kadseofilters.shop_id', '=', 'shops.id')
 ->where('shops.site_id', '=', CURRENT_SITE); $aoKadseofilters = $oKadseofilters->findAll(FALSE);

А далее срабатывал код, который помещал нужные данные в карту сайта:

foreach ($aoKadseofilters as $oKadseofilter) 
{
 $object->addNode($oKadseofilter->href(), 2, 0.5); 
}

В данном случае очень важно учесть, что из всего этого большого объема данных, нам нужна реально только ссылка на URL*. А мы получаем все данные. Оптимизировать можно, получая эти данные по частям. Получаем часть, добавляем узлы, извлекая полезную информацию - url, получаем следующую часть и так далее до последнего. Таким образом используем меньший объем оперативной памяти. В итоге получаем код:

// Get max ID
$oCore_QueryBuilder_Select = Core_QueryBuilder::select(array('MAX(kadseofilters.id)', 'max_id'));
$oCore_QueryBuilder_Select
 ->from('kadseofilters')
 ->join('shops', 'kadseofilters.shop_id', '=', 'shops.id')
 ->where('shops.site_id', '=', CURRENT_SITE)
 ->where('kadseofilters.deleted', '=', 0);

$aRow = $oCore_QueryBuilder_Select->execute()->asAssoc()->current();

$maxId = $aRow['max_id'];

$iFrom = 0;
$iOnStep = 1000;

do {
 $oKadseofilters = Core_Entity::factory('kadseofilter');
 $oKadseofilters->queryBuilder()
  ->join('shops', 'kadseofilters.shop_id', '=', 'shops.id')
  ->where('kadseofilters.id', 'BETWEEN', array($iFrom + 1, $iFrom + $iOnStep))
  ->where('shops.site_id', '=', CURRENT_SITE);

 $aoKadseofilters = $oKadseofilters->findAll();

 foreach ($aoKadseofilters as $oKadseofilter)
 {
  $object->addNode($oKadseofilter->href(), 2, 0.5);
 }

 $iFrom += $iOnStep;
}
while ($iFrom < $maxId);

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

$aoKadseofilters = $oKadseofilters->findAll(FALSE);

Таким образом мы значительно уменьшили необходимый для работы скрипта объем ОЗУ и все заработало. 

 

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

 

* здесь и далее URL - сущность модуля SEO-фильтр, url - путь к выборке
comments powered by Disqus


Следующий "Изменения в направлении "Доработка сайтов"" К списку Предыдущий "Мобильное приложение для вашего магазина на HostCMS"