Posting in the Magento forums has been disabled pending the implementation of a new and improved forum solution which should better serve the community.

For new questions please post at magento.stackexchange.com, the community-run support site for the Magento community. We will be providing updates on the new forum solution soon. For questions or concerns please email community@magento.com.

Magento Forum

Page 1 of 2
Memory leak при переборе большого количества товаров
 
akoval
Member
 
Total Posts:  50
Joined:  2011-03-19
 

Столкнулся с одной проблемой сегодня.

Есть магазин с большим количеством товаров (более 100000). Нужно в цикле пройти по всем товарам и записать их аттрибуты в текстовый файл (нечто вроде импорта)
Написал скрипт, работающий из командной строки в котором
беру простым SQL запросом из базы данных Магенто список id товаров (порциями по 1000 штук).
А потом иду в цикле по каждой порции и загружаю товар по его id.

foreach ($products as $v){
                $product 
Mage::getModel('catalog/product')->load($v["product_id"]);
                
$product->clearInstance();
                unset(
$product);
            
}

Методом тыка определил, что если просто иду по циклу без загрузки товара - утечек пямяти нет, все ID товаров перебираются.

Пока не поставил clearInstance и unset, лимит памяти на моем сервере исчерпывался примерно после 11000 товаров.
Поставил очистку - стал слетать по лимиту памяти примерно на 55000 товаров.

Может кто знает, что за фигня, память не чистится.
Версия магенто последняя - 1.7.0.2.

Спасибо

 
Magento Community Magento Community
Magento Community
Magento Community
 
Rugento
Guru
 
Avatar
Total Posts:  540
Joined:  2008-11-15
Russia, Vologda
 

Память может утекать как из-за скрипта, объекты коллекций и базы все равно висят и копятся, так и из-за сборщика мусора PHP.
самое надежное грузить sql прямыми запросами, второе грузить коллекцию с нужными атрибутами и пагером с limit.
Грузить каждый раз создавая модель это не гут, много всего тянется.
Либо, если очень хочется так, то грузить только нужные атрибуты

$product Mage::getModel(\'catalog/product\')->load($v[\"product_id\"], array(\'color\'\'size\'\'etc...\'));
 
Magento Community Magento Community
Magento Community
Magento Community
 
akoval
Member
 
Total Posts:  50
Joined:  2011-03-19
 

Спасибо большое.

В самом начале у меня так и было:

$product Mage::getModel('catalog/product');
foreach (
$products as $v){
                $product
->load($v["product_id"]);
                
$product->clearInstance();

unset($product);
Те же я@ца, только в профиль… Только предел памяти исчерпывается после 11000 товаров

Вариант в виде:

$product Mage::getModel('catalog/product');;
foreach (
$products as $v){
//                $product->load($v["product_id"]);
//                $product->clearInstance();

unset($product);
ни дает никакой утечки памяти, т.е. в остальной части моего скрипта все должно быть в порядке.
 
Magento Community Magento Community
Magento Community
Magento Community
 
akoval
Member
 
Total Posts:  50
Joined:  2011-03-19
 

Проверил ваш вариант с указанием требемых атрибутов - $product-->load($id, array(.....)); - указал price, visibility

Столкнулся с интересным эффектом:
- потребление памяти растет, но гораздо медленней. Если раньше у меня слетало на 55000 товаров, то сейчас прошелся по всем 112000 товаров, причем, запас по памяти остался еще тысяч на 50000 товаров (это примерно, я смотрю по top команде).

НО самое интересное не это. У меня выполняется два прохода по полному списку товаров. Т.е. первый проход у меня выполнился полностью (112000 товаров) и я добрался до второго прохода. Невероятно, но факт - при втором проходе по всем товарам (тот же кусок кода) - потребление памяти абсолютно не увеличивается. Получается, что где-то существует нечто вроде кэша… Вот как бы его найти и вычистить…
Процесс закончился - на втором проходе никакого роста памяти…

 
Magento Community Magento Community
Magento Community
Magento Community
 
Rugento
Guru
 
Avatar
Total Posts:  540
Joined:  2008-11-15
Russia, Vologda
 

Скорее всего, объекты атрибутов и их значений остаются в памяти, они не привязаны к catalog/product поэтому не удаляются вместе с ним.
Почистить надо потрудиться) хотя вроде как смысла нет, сборщик мусора PHP сделает это после выполнения скрипта.

 
Magento Community Magento Community
Magento Community
Magento Community
 
akoval
Member
 
Total Posts:  50
Joined:  2011-03-19
 

Придется через shell-скрипт выкручиваться.

Спасибо за сочувствие.

 
Magento Community Magento Community
Magento Community
Magento Community
 
Sergiy Stotskiy
Member
 
Avatar
Total Posts:  53
Joined:  2011-02-27
 

Извините, но это мягко говоря бред:

$product Mage::getModel('catalog/product');
foreach (
$products as $v){
                $product
->load($v["product_id"]);
                
$product->clearInstance();

unset($product);

Для перебора больших коллекций есть fetchItem. Думаю с версии 1.5.1 она доступна.
Пользоваться ею нужно так:

$collection Mage::getModel('catalog/product')->getCollection()
  ->
addAttributeToSelect('*')
  ->
addFieldToFilter('id', array('in' => $product_ids));
while (
$product $collection->fetchItem()) {
  
// do something there 
  
$product->clearInstance();
}

Больше информации можно получить если почитать вот там: http://freaksidea.com/php_and_somethings/show-61-magento-modeli-ot-a-do-ia-3-kita

 
Magento Community Magento Community
Magento Community
Magento Community
 
Rugento
Guru
 
Avatar
Total Posts:  540
Joined:  2008-11-15
Russia, Vologda
 

Не извиним))
Мягко говоря fetchItem делает выборку только из таблицы catalog_product_entity и атрибуты не подгружает, то есть для наших целей она не подойдет.

$collection Mage::getModel('catalog/product')->getCollection()
      ->
addAttributeToSelect('*')
      ->
addIdFilter('140');
    
$product $collection->fetchItem();

array(
9{
  [
"entity_id"=> string(3"140"
  
["entity_type_id"=> string(2"10"
  
["attribute_set_id"=> string(2"58"
  
["type_id"=> string(6"simple"
  
["sku"=> string(7"2gbdimm"
  
["created_at"=> string(19"2008-07-25 00:59:55"
  
["updated_at"=> string(19"2008-07-25 02:14:23"
  
["has_options"=> string(1"0"
  
["required_options"=> string(1"0"
}

VS.

$collection Mage::getModel('catalog/product')->getCollection()
      ->
addAttributeToSelect('*')
      ->
addIdFilter('140');
    
$product $collection->getFirstItem();

array(
33{
  [
"entity_id"=> string(3"140"
  
["entity_type_id"=> string(2"10"
  
["attribute_set_id"=> string(2"58"
  
["type_id"=> string(6"simple"
  
["sku"=> string(7"2gbdimm"
  
["created_at"=> string(19"2008-07-25 00:59:55"
  
["updated_at"=> string(19"2008-07-25 02:14:23"
  
["has_options"=> string(1"0"
  
["required_options"=> string(1"0"
  
["meta_keyword"=> string(0""
  
["description"=> string(572"Crucial 2GB PC4200 DDR2 533MHz Memory
Crucial Technologies is part of Micron, the largest DRAM manufacturer in the U.S. and one of the largest in the world. Because they actually manufacture memory, they have unparalleled expertise in the upgrade industry. Crucial is the only consumer memory upgrade supplier that is part of a major DRAM manufacturer.

Every single Crucial memory module is tested before it goes out their doors. Crucial chips are individually tested under varying temperatures, voltages, and operational conditions for performance and functionality. "
  
["short_description"=> string(572"Crucial 2GB PC4200 DDR2 533MHz Memory
Crucial Technologies is part of Micron, the largest DRAM manufacturer in the U.S. and one of the largest in the world. Because they actually manufacture memory, they have unparalleled expertise in the upgrade industry. Crucial is the only consumer memory upgrade supplier that is part of a major DRAM manufacturer.

Every single Crucial memory module is tested before it goes out their doors. Crucial chips are individually tested under varying temperatures, voltages, and operational conditions for performance and functionality. "
  
["custom_layout_update"=> string(0""
  
["status"=> string(1"1"
  
["tax_class_id"=> string(1"1"
  
["visibility"=> string(1"4"
  
["manufacturer"=> string(3"111"
  
["vendoryml"=> string(1"0"
  
["weight"=> string(6"1.0000"
  
["price"=> string(8"199.9900"
  
["name"=> string(37"Crucial 2GB PC4200 DDR2 533MHz Memory"
  
["url_key"=> string(37"crucial-2gb-pc4200-ddr2-533mhz-memory"
  
["gift_message_available"=> string(0""
  
["ram_size"=> string(3"2GB"
  
["meta_title"=> string(0""
  
["meta_description"=> string(0""
  
["thumbnail"=> string(46"/c/r/crucial-2gb-pc4200-ddr2-533mhz-memory.jpg"
  
["small_image"=> string(46"/c/r/crucial-2gb-pc4200-ddr2-533mhz-memory.jpg"
  
["image"=> string(46"/c/r/crucial-2gb-pc4200-ddr2-533mhz-memory.jpg"
  
["custom_design"=> string(0""
  
["options_container"=> string(10"container2"
  
["url_path"=> string(42"crucial-2gb-pc4200-ddr2-533mhz-memory.html"
  
["stock_item"=> object(Varien_Object)#160 (7) {
    
["_data":protected] => array(1{
      [
"is_in_stock"=> NULL
    }
    [
"_hasDataChanges":protected] => bool(false)
    
["_origData":protected] => NULL
    [
"_idFieldName":protected] => NULL
    [
"_isDeleted":protected] => bool(false)
    
["_oldFieldsMap":protected] => array(0{
    }
    [
"_syncFieldsMap":protected] => array(0{
    }
  }
}

Разница, вроде как, есть)

 
Magento Community Magento Community
Magento Community
Magento Community
 
akoval
Member
 
Total Posts:  50
Joined:  2011-03-19
 

Добрый день!

Дело в том, что при использовании fetchItem все равно нужно будет выполнять операцию загрузки товара (load), так как выше уже отметили, что необходимые атрибуты не грузятся по умолчанию.

А при загрузке товара все равно происходит утечка, я уже приводид данные выше.

С уважением.

 
Magento Community Magento Community
Magento Community
Magento Community
 
akoval
Member
 
Total Posts:  50
Joined:  2011-03-19
 

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

Аналогичная проблема с генераций sitemap.xml

[Wed May 22 12:56:26 2013] [error] [client 94.70.241.39] PHP Fatal error:  Allowed memory size of 268435456 bytes exhausted (tried to allocate 74 bytesin /var/www/mage_store/app/code/core/Mage/Sitemap/Model/Resource/Catalog/Product.php on line 186refererhttp://www.domain.com/index.php/admin/sitemap/index/key/1c0fc48fa96dec310d1c9f71796ad59e/

В магазине более 100000 товаров. Памяти не хватает именно когда пытается добавить товары в sitemap.xml (я проверил, если закомментарить выдачу товаров - все заканчивается нормально).
Админ панель умирает. 512 мегабайт памяти для PHP не спасает. Разработчики магенто вообще в курсе того, что есть такая проблема?
Там же элементарный перебор коллекции всех товаров.

Я понимаю, что sitemap.xml имеет свои ограничения (не более 50000 итемов и не более 10 мегабайтов), но я очень сомневаюсь, что и 50000 товаров в один файл выдаст.

Кто что может посоветовать?
Спасибо....

 
Magento Community Magento Community
Magento Community
Magento Community
 
Rugento
Guru
 
Avatar
Total Posts:  540
Joined:  2008-11-15
Russia, Vologda
 

А какая версия PHP на сервере?

 
Magento Community Magento Community
Magento Community
Magento Community
 
akoval
Member
 
Total Posts:  50
Joined:  2011-03-19
 

Спасибо.

OS - Centos5.9 (64)
PHP 5.3.23 (built: Mar 15 2013 11:15:15)

Magento CE 1.7.0.2

Вот на этом куске кода валится с “Allowed memory size of 268435456 bytes exhausted” (класс Mage_Sitemap_Model_Sitemap)

/**
     * Generate XML file
     *
     * @return Mage_Sitemap_Model_Sitemap
     */
    public function generateXml()
    
{
      
. . . . . 
        
/**
         * Generate products sitemap
         */
        
$changefreq = (string)Mage::getStoreConfig('sitemap/product/changefreq'$storeId);
        
$priority   = (string)Mage::getStoreConfig('sitemap/product/priority'$storeId);
        
$collection Mage::getResourceModel('sitemap/catalog_product')->getCollection($storeId);
        foreach (
$collection as $item{
            $xml 
sprintf('<url><loc>%s</loc><lastmod>%s</lastmod><changefreq>%s</changefreq><priority>%.1f</priority></url>',
                
htmlspecialchars($baseUrl $item->getUrl()),
                
$date,
                
$changefreq,
                
$priority
            
);
            
$io->streamWrite($xml);
        
}
        
unset($collection);
       . . . . . . 
    
}
 
Magento Community Magento Community
Magento Community
Magento Community
 
Rugento
Guru
 
Avatar
Total Posts:  540
Joined:  2008-11-15
Russia, Vologda
 

Не так давно озаботился этим вопросом тоже)
видимо дело в сборке мусора в PHP
http://stackoverflow.com/questions/584960/whats-better-at-freeing-memory-with-php-unset-or-var-null

Переводец:

unset() делает то, что его имя говорит - сбросить переменную. Это не приведет к немедленному освобождению памяти. Сборщик мусора PHP будет делать это, когда это увидеть))

Если вы делаете все, что $ = NULL; то вы переписывания данных переменной. Вы можете получить памяти освободил / сократился быстрее, но он может украсть циклов центрального процессора от кода, который действительно нуждается в них раньше, в результате чего больше общего времени выполнения.

т.е. unset не освобождает память сразу, а null может освободить.
можно поэкспериментировать + 256 маловато для 100000 товаров, вроде.

 
Magento Community Magento Community
Magento Community
Magento Community
 
akoval
Member
 
Total Posts:  50
Joined:  2011-03-19
 

Спасибо, большое!

Правда не представляю, как это мне может помочь?…

Тут явно берутся все товары и возвращаются одним массивом:

class Mage_Sitemap_Model_Resource_Catalog_Product extends Mage_Core_Model_Resource_Db_Abstract
{
.  .  .  .  .  .
    
/**
     * Get category collection array
     *
     * @param unknown_type $storeId
     * @return array
     */
    
public function getCollection($storeId)
    
{
        $products 
= array();

        
$store Mage::app()->getStore($storeId);
        
/* @var $store Mage_Core_Model_Store */

        
if (!$store{
            
return false;
        
}

        $urCondions 
= array(
            
'e.entity_id=ur.product_id',
            
'ur.category_id IS NULL',
            
$this->_getWriteAdapter()->quoteInto('ur.store_id=?'$store->getId()),
            
$this->_getWriteAdapter()->quoteInto('ur.is_system=?'1),
        );
        
$this->_select $this->_getWriteAdapter()->select()
            ->
from(array('e' => $this->getMainTable()), array($this->getIdFieldName()))
            ->
join(
                array(
'w' => $this->getTable('catalog/product_website')),
                
'e.entity_id=w.product_id',
                array()
            )
            ->
where('w.website_id=?'$store->getWebsiteId())
            ->
joinLeft(
                array(
'ur' => $this->getTable('core/url_rewrite')),
                
join(' AND '$urCondions),
                array(
'url' => 'request_path')
            );

        
$this->_addFilter($storeId'visibility'Mage::getSingleton('catalog/product_visibility')->getVisibleInSiteIds(), 'in');
        
$this->_addFilter($storeId'status'Mage::getSingleton('catalog/product_status')->getVisibleStatusIds(), 'in');

        
$query $this->_getWriteAdapter()->query($this->_select);
        while (
$row $query->fetch()) {
            $product 
$this->_prepareProduct($row);
            
$products[$product->getId()$product;
        
}

        
return $products;
    
}

    
/**
     * Prepare product
     *
     * @param array $productRow
     * @return Varien_Object
     */
    
protected function _prepareProduct(array $productRow)
    
{
        $product 
= new Varien_Object();
        
$product->setId($productRow[$this->getIdFieldName()]);
        
$productUrl = !empty($productRow['url']) ? $productRow['url''catalog/product/view/id/' $product->getId();
        
$product->setUrl($productUrl);
        return 
$product;
    
}
}
А фатальная ошибка (память кончается) как раз в функции __prepareProduct и происходит. Товаров реально больше 140000
Хоть бери и переписывай siteMap…
 
Magento Community Magento Community
Magento Community
Magento Community
 
Rugento
Guru
 
Avatar
Total Posts:  540
Joined:  2008-11-15
Russia, Vologda
 

Да, точно, вся коллекция собирается в массив.
Тут без переделки не обойтись.

 
Magento Community Magento Community
Magento Community
Magento Community
 
akoval
Member
 
Total Posts:  50
Joined:  2011-03-19
 

Я экспорт сделал через связку shell-script+php_cli по интервалам id товаров. (1-5000,5001-10000,....)
Больше не влезало. Так там я уверен был, что каждый раз память с чистого листа.

А здесь скорее всего никак не победить без полного переписывания начинки Sitemap.php.
Там еще и AW_Blog торчит, который Sitemap.php переопределяет.

А потом надо решать проблему 50000 линков в одном файле sitemap.xml и макс размер файла не более 10Мбайт.

Предложил клиенту расширение от MageWorx, там вроде бы уже все предусмотрели. Пусть думает.

Спасибо за сочувствие.
С уважением

 
Magento Community Magento Community
Magento Community
Magento Community
Magento Community
Magento Community
Back to top
Page 1 of 2