Šiukšlių rinkimas PHP aplinkoje

Tikriausia kiekvienas PHP programuotojas, kuris yra susidūręs su ilgai vykdomais sunkiais procesais, pastebėjo, kad PHP programa nuolat lėtėja. Itin gerai tai žino Magento integravę žmonės, kai reikia importuoti didelį kiekį prekių iš išorinių šaltinių. Tai įvyksta todėl, kad neefektyviai naudojama operatyvioji atmintinė, kurioje kaupiasi šiukšlės ir trikdo programos darbą su atmintimi, blogiausiu atveju programa nutraukia darbą, nes viršyja numatytą didžiausią atminties kiekį.
Kai kurie tikriausia pagalvojo – Magento… Teisingai! Tikrai opi problema, kuriai atrodo, kad nėra sprendimo. Taigi apie tai ir pakalbėkime.

Tipinis prekių importavimo į Magento sistemą kodas:

Mage::app();
date_default_timezone_set('Europe/Vilnius');
Mage::app()->setCurrentStore(0);
$product = Mage::getModel('catalog/product');
/**
* veiksmai pagal jūsų situaciją
**/
$product->load($product_id);
$product->setStockData($stockData);
$product->setCategoryIds($ids);
$product->save();

Sistemėlė paprasta, tačiau mano atveju ~20 000 prekių importuojasi apie 1.5 valandos (serveris: 60% CPU, 1GB RAM). Esant daug didesniems kiekiams reikia ir didesnių resursų ir daugiau laiko.
Internete radau vieną būdą pagreitinti importavimą, reikia paredaguoti /lib/Varien/Db/Select.php failą taip:
Originalas:

public function __construct(Zend_Db_Adapter_Abstract $adapter)
    {
        parent::__construct($adapter);
        self::$_joinTypes[] = self::STRAIGHT_JOIN_ON;
        self::$_partsInit = array(self::STRAIGHT_JOIN => false) + self::$_partsInit;
    }

Modifikuotas:

public function __construct(Zend_Db_Adapter_Abstract $adapter)
    {
        parent::__construct($adapter);
        if (!in_array(self::STRAIGHT_JOIN_ON, self::$_joinTypes))
        self::$_joinTypes[] = self::STRAIGHT_JOIN_ON;
        self::$_partsInit = array(self::STRAIGHT_JOIN => false) + self::$_partsInit;
    }

Pagreitėjo, tačiau siektino efekto nedavė, sistemai dirbant atminties sunaudojimas ir toliau augo…
Tačiau su PHP 5.3 atsirado galimybė „forsuoti“ šiukšlių surinkimą. http://www.php.net/manual/en/features.gc.collecting-cycles.php
Taigi papildome savo prekių importavimo sistemėlę:

Mage::app();
date_default_timezone_set('Europe/Vilnius');
Mage::app()->setCurrentStore(0);
$product = Mage::getModel('catalog/product');
/**
* veiksmai pagal jūsų situaciją
**/
$product->load($product_id);
$product->setStockData($stockData);
$product->setCategoryIds($ids);
$product->save();
if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
    gc_collect_cycles();
}

Rezultatas tiesiog puikus! Importavimas užtruko ~10 minučių, atminties sunaudojimas neperkopė 4M ribos. Tačiau nereikia pamiršti, kad savo kode taip pat turite palaikyti tvarką: sunaikinti kintamuosius ar masyvus kurie yra nebereikalingi, neapsikrauti nereikalingais duomenimis.
Bandymo rezultatai grafikuose, kur 1. – Prekių importavimas, 2. – Modifikuotas importavimas:

CPU usage - by day


MySQL queries - by day

Žygimantas

Lead developer at Soundest. Zend Certified Engineer.

More Posts - Website

Žymos: , , ,

12 komentarų

  1. Neodan

    Neblogas straipsniukas. Koki softa naudoji stebeti serverio apkrovoms?

  2. Munin http://munin-monitoring.org, bet noriu ką nors kitką pabandyti. Įdomiai atrodo cacti, tačiau by default siūlo apache įsidiegti, o aš naudoju nginx. Kabinti ant kito porto kažkaip nesinori, o laiko paieškoti sprendimams nelabai šiuo metu yra… Tikėkimės ateityje laiko atsiras.

  3. Šiaip rekomeduočiau naudoti

    $product = Mage::getModel('catalog/product');
    // dadema sita eilute
    $product->clearInstance();

    Ir visiems laimė ir php-5.2 ir 5.3

    • Nepastebėjau, kad ką naudingo duotu RAM/CPU suvartojime.

      • Na siuo atveju RAM/CPU sunaudojimas lemia ir my.cnf esantys teisingi prametrai ypac innodb_ skyrelyje.
        Taip pat reikalingas siok toks pakoregavimas Product.php faile
        Ir tada viskas sukasi normaliai jei naudoji 5.2 versija php.
        Jei domina platesne informacija kreipkites.

        • Kaip tik šiuo atveju nieko nelemia my.cnf koregavimas, nes problemą sukelia PHP proceso sunaudojama RAM atmintis. Koreguoti pačio magento failus – nepatartina, nes kai ateis atnaujinimo laikas – to padaryti tiesiog negalėsi.

          • Dėl pačių magento failų redagavimo – taip nerekomenduotina.
            Tačiau …. (-; Jeigu būtų logiškas, veikiantis būdas spręsti kažkokią tai problemą, tai manau kai kurie juo pasinaudotų, atitinkamai įtraukdami papildomos funkcijos aptarnavimą atnaujinimo metu.

            Aš bent jau išbandyčiau ar yra koks nors efektas, ką pakeitimai iš tikro atlieka (ar nesugriauna kažkokių reikalingų funkcijų), o paskui galvočiau kas man svarbiau ar greitesnis duomenų import’as, ar papildomas darbas magento atnaujinimo metu (-;

          • Na tokiems atvejams jei nezinojote yra app/local directorija :)
            core keisti nereikia sutinku, tam tikslui yra local directorijoje atkuriama mage struktura ir siuo atveju atnaujinimai jokios itakos neturi. Atnaujinimai vyksta sau, o jusu kodas lieka local direktorijoje nepakites, Ir jei reikia korekcijos patikrines relizo info gali pasikoreguoti.
            Tai tiek trumpai.
            del my.cnf irgi nelabai teisus ponuli cpu itakoja ir nemazaia bei greitaveikai magentos.

          • Jei problemą sprendžia RAM optimizavimas PHP pusėje, tai papasakok kodėl viskas pagreitėja nepakeitus DB nustatymų? DB pusė visa buvo prioptimizuota iki negaliu, nebebuvo kur trauktis, tad ėmiausi PHP… Rezultatas puikus su pora papildomų eilučių kodo. Nereikia nieko nei extendinti, nei DB toliau kankinti. Juolab ne DB pusėje bottleneck.

          • Kaip minejau as kalbu apie 5.2 versija php. Del 5.3 nesinginciju nes nebandziau dar laiko nebuvo.
            Kita savaite bandysiu ant 5.3 tada galima bus placiau.
            Siuo atveju kalba eina apie 5.2 versijai galimus sprendimus.

          • Besiginčydamas praleidau šį faktą :)

        • Na domina platesnė infomarijca. Visi metodai, kurie gali spręsti lėtumo problemą. Jeigu galima plačiau čia pakomentuoti arba forume, arba galite parašyti atskirą straipsnį (-;

Parašykite komentarą

El. pašto adresas nebus skelbiamas. Būtini laukeliai pažymėti *

Galite naudoti šias HTML žymas ir atributus: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>