Web pro horních 10 000 www.koncier.cz
Zobrazují se odpovědi 1 až 15 z 15

Pomaly update pre 8 tisic produktov

  1. Ahojte. Prvykrat sa hram s trochu vacsimi databazami a importami.

    Mam 8 tisic produktov, pre ktore musim urobit update skladu z 2 xml suborov. Celkovo bude mat databaza priblizne 50 tisic produktov.

    PHP kód:
    foreach($xml->PRODUCTS->PRODUCT as $items) {

                 
    $quantity1 0;
                 
    $quantity2 0;
                 
    $quantity3 0;

                 
    $productId $security->secureString($items->ID);  
                 
    $quantity1 $security->secureInteger($items->QUANTITY);

                 
    $j 0;
                 foreach (
    $items->ARRIVAL as $arrival) {
                    
    $j++;
                    if (!empty(
    $arrival->QUANTITY)) {
                        if (
    $j == 1) {
                            
    $quantity2 $security->secureInteger($arrival->QUANTITY);
                        } else {
                            
    $quantity3 $security->secureInteger($arrival->QUANTITY);
                        } 
                    }
                 }


                    
    $query "UPDATE mb_variant SET stock1 = ?, stock2 = ?, stock3 = ? WHERE product_number = ? AND web = ? AND import = ?";
                    
    $stmt $this->db->prepare($query);
                    
    $stmt->bind_param('iiisii'$quantity1$quantity2$quantity3$productId$web$import);
                    echo 
    $stmt->error;
                    
    $stmt->execute(); 
                    
    $stmt->fetch();
                    
    $stmt->free_result();
                    
    $stmt->close(); 


    Prve xml mi prejde priblizne za 640 sekund
    Druhe xml priblizne za 800 sekund

    Polia product_number, web, import mam v databaze indexovane. Potreboval by som tuto operaciu zrychlit. Je na to nejake rychlejsie riesenie?

  2. Co se právě děje na Webtrhu?
  3. Tak teoreticky by jsi mohl ten prepare dát už před ten foreach, ať ho zbytečně neděláš znovu v každém kroku.
    Kód:
    $query = "UPDATE mb_variant SET stock1 = ?, stock2 = ?, stock3 = ? WHERE product_number = ? AND web = ? AND import = ?"; 
    $stmt = $this->db->prepare($query); 
    $stmt->bind_param('iiisii', $quantity1, $quantity2, $quantity3, $productId, $web, $import);
    
    foreach($xml->PRODUCTS->PRODUCT as $items) { 
    
                 $quantity1 = 0; 
                 $quantity2 = 0; 
                 $quantity3 = 0; 
    
                 $productId = $security->secureString($items->ID);   
                 $quantity1 = $security->secureInteger($items->QUANTITY); 
    
                 $j = 0; 
                 foreach ($items->ARRIVAL as $arrival) { 
                    $j++; 
                    if (!empty($arrival->QUANTITY)) { 
                        if ($j == 1) { 
                            $quantity2 = $security->secureInteger($arrival->QUANTITY); 
                        } else { 
                            $quantity3 = $security->secureInteger($arrival->QUANTITY); 
                        }  
                    } 
                 }
                 $stmt->execute(); 
    } 
    $stmt->close();

  4. 1) nedělat update když nemusíš - před updated si stáhnout do mapy všechny záznamy a přetransformovat (třeba spojit všechny updatované pole do stringu a udělat z nich hash) proti které porovnávat s novým stavem

    2) Nahradit iterátory klasickám for cyklem s indexy (iterátor je docela žrout prostředků a taky vidím v druhém cyklu stejně je třeba index)

    3) změřit jak dlouho trvá parsování xml před samotným vložením a transformovat ho v primitivní pole namísto DOM objektu

    4) pre-procesing vstupního souboru - vyházet data co nejsou třeba ještě před spouštěním skriptu.

    5) updatovat jenom objekt v paměti a ten pak nacpat do db v jediném dotazu

  5. Prepare lze dát přes foreach, ale již nelze dát ten bind, používají se tam hodnoty z foreach.

    předpokládám, že db je mysql/mariadb.

    z kraje máš asi 3 možnosti:
    1) použít multi update. Mysql to přímo neumí, ale lze využít trik s insert on duplicate key:
    PHP kód:

    // funkce pro vytvoření sql pro hromadná update
    function update_multi($db$array_of_values) {
        
    // předpokládám (a zároveň vyžaduji index přes 3 sloupce - product_number, web, import
        // insert on duplicate key update má dokumentaci tady https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
        // píšu kód z hlavy, bude asi potřeba ho lehce upravit, ber to jako inspiraci, nejsem php programátor

        // vytvoří sadu placeholderů (?,?,?,?,?),(?,?,?,?,?),(?,?,?,?,?),(?,?,?,?,?),(?,?,?,?,?)
        // výsledkem bude jediný dotaz, kde jsou všechny hodnoty pro update. Každá závorka představuje jeden záznam
        // první 3 otazníky symbolizují unikátní klíč, který by měl být vytvořený přes sloupce product_number, web, import
        // pokud záznam již existuje (což je právě předpoklad tohoto dotazu, lze ošetřit ještě where in podmínkou
        // aplikuje se část za ON DUPLICATE KEY UPDATE a u řádků, které jsou určeny tím unikátním klíče se změní pouze sloupce stockX
        
    $placeholders implode(","array_fill(0count($array_of_values) - 1"(?,?,?,?,?)"));
        
    $query "INSERT INTO mb_variant (product_number, web, import, stock1, stock2, stock3) VALUES $placeholders
                        ON DUPLICATE KEY UPDATE stock1=VALUES(stock1),stock2=VALUES(stock2),stock3=VALUES(stock3);"
    ;

        
    $stmt $db->prepare($query);
        
    $stmt->bind_param(str_repeat('iiisii'count($array_of_values)), array_reduce($array_of_values'array_merge', array()););
        echo 
    $stmt->error;
        
    $stmt->execute();
        
    $stmt->fetch();
        
    $stmt->free_result();
        
    $stmt->close();
    }

    $bulk_values = array();
    foreach(
    $xml->PRODUCTS->PRODUCT as $items) { 

                 
    $quantity1 0
                 
    $quantity2 0
                 
    $quantity3 0

                 
    $productId $security->secureString($items->ID);   
                 
    $quantity1 $security->secureInteger($items->QUANTITY); 

                 
    $j 0
                 foreach (
    $items->ARRIVAL as $arrival) { 
                    
    $j++; 
                    if (!empty(
    $arrival->QUANTITY)) { 
                        if (
    $j == 1) { 
                            
    $quantity2 $security->secureInteger($arrival->QUANTITY); 
                        } else { 
                            
    $quantity3 $security->secureInteger($arrival->QUANTITY); 
                        }  
                    } 
                 } 

                 
    // připraví 100 položek do jednoho pole k poslání v jednom dotazu
                 // pokud se počet dostane na 100, zavolá sql a pole vynuluje, aby se mohlo plnit znovu
                 
    $bulk_values[] = array($productId$web$import$quantity1$quantity2$quantity3);
                 if(
    count($bulk_values) === 100) {
                     
    update_multi($this->db$bulk_values);
                     
    $bulk_values = array();
                 }
    }  

    if(
    count($bulk_values) > 0) {
        
    update_multi($this->db$bulk_values);
        
    $bulk_values = array();

    2) druhá možnost je prostě odstranit indexy, udělat update a pak tam znovu vložit indexy. Při každém updatu se totiž musí aktualizovat index a to je drahá operace.


    3) existuje možnost, že nemáš v pořádku vytvořené indexy a samotný select * from mb_variant where product_number = ? and web = ? and import = ? nepoužívá index a trvá příliš dlouho a tím združuje update každého řádku, to si ověř.

  6. Citace Původně odeslal weerwolf Zobrazit příspěvek
    2) Nahradit iterátory klasickám for cyklem s indexy (iterátor je docela žrout prostředků a taky vidím v druhém cyklu stejně je třeba index)
    Iterator neni o tolik vetsi zrout prostredku, aby to melo smysl resit. Mozna u milionu opakovani... Mozna...

  7. Citace Původně odeslal Aleš Jiříček Zobrazit příspěvek
    Iterator neni o tolik vetsi zrout prostredku, aby to melo smysl resit. Mozna u milionu opakovani... Mozna...
    V tom si dovolím oponovat - optimalizační triky mají vždy smysl. Když nic tak si programátor uvědomí jak ve skutečnosti funguje procesor a jak se zpracovávají instrukce (i když tady jde polemizovat jaký to má smysl v interpretovaném jazyku, který už je dost daleko od HW a zároveň jde nalézt hromadu use-case, kde bude iterátor rychlejší díky just in time optimalizaci) a jak fungují kompilátory. Ono i kdyby úspora byla jenom jedna ms na cyklu tak to bude o 8 vteřin na celém zpracování bez jakékoliv námahy jenom s trochou přemýšlení jaké mají Cčkaři.

  8. Citace Původně odeslal weerwolf Zobrazit příspěvek
    V tom si dovolím oponovat - optimalizační triky mají vždy smysl. Když nic tak si programátor uvědomí jak ve skutečnosti funguje procesor a jak se zpracovávají instrukce (i když tady jde polemizovat jaký to má smysl v interpretovaném jazyku, který už je dost daleko od HW a zároveň jde nalézt hromadu use-case, kde bude iterátor rychlejší díky just in time optimalizaci) a jak fungují kompilátory. Ono i kdyby úspora byla jenom jedna ms na cyklu tak to bude o 8 vteřin na celém zpracování bez jakékoliv námahy jenom s trochou přemýšlení jaké mají Cčkaři.
    Aleš má pravdu.

    Optimalizaci musíš vždy dělat až na základě měření a nikoliv dopředu jen protože ti příjde užitečná. Tady se jedná o 8 000 položek, reálný rozdíl bude minimální (sám píšeš 8s, což je 1% zlepšení výkonu). Optimalizace zpravidla snižuje čitelnost kódu a zpravidla je závislá na konkrétní implementaci, verzi php, tu tady neznáme.

    Nezpochybňuji tvoje tipy a nápady na zlepšení, ale nemyslím si, že v tomhle případě mohou za výrazné zdržení času zpracování. Určitě by senior programátoři měli znát vnitřnosti a umět je řádně využít ve svůj prospěch. Bacha na to, php nemá JIT (just in time), chystá se až do verze 8, zatím je hodně experimentální, je staticky kompilované při spuštění.

  9. Citace Původně odeslal weerwolf Zobrazit příspěvek
    V tom si dovolím oponovat - optimalizační triky mají vždy smysl. Když nic tak si programátor uvědomí jak ve skutečnosti funguje procesor a jak se zpracovávají instrukce (i když tady jde polemizovat jaký to má smysl v interpretovaném jazyku, který už je dost daleko od HW a zároveň jde nalézt hromadu use-case, kde bude iterátor rychlejší díky just in time optimalizaci) a jak fungují kompilátory. Ono i kdyby úspora byla jenom jedna ms na cyklu tak to bude o 8 vteřin na celém zpracování bez jakékoliv námahy jenom s trochou přemýšlení jaké mají Cčkaři.
    az na to ze uspora 1ms na iteraci v zadnem pripade nemuze nastat, protoze jedna iterace zabere sotva nekolik mikrosekund (na rychlejsich strojich ani to ne) a to jak v pripade foru tak v pripade foreache... takze na 8000 polozkach usetris radove milisekundy v tom nejlepsim pripade... takze asi tak 0,001% behu celeho kodu...

    nekdy je treba nad optimalizacemi taky trochu premyslet... A zneprehlednit si kod pro usporu kterou nikdo nikdy nema sanci poznat je vazne jen padle na hlavu, nic vic... A ja si vzdycky rikam jaky lidi pisou takovy debilni kody... tak uz mam jeden exemplar :)
    Naposledy upravil Aleš Jiříček : 10.07.2018 v 15:38

  10. Citace Původně odeslal Aleš Jiříček Zobrazit příspěvek
    az na to ze uspora 1ms na iteraci v zadnem pripade nemuze nastat, protoze jedna iterace zabere sotva nekolik mikrosekund (na rychlejsich strojich ani to ne) a to jak v pripade foru tak v pripade foreache... takze na 8000 polozkach usetris radove milisekundy v tom nejlepsim pripade... takze asi tak 0,001% behu celeho kodu...

    nekdy je treba nad optimalizacemi taky trochu premyslet... A zneprehlednit si kod pro usporu kterou nikdo nikdy nema sanci poznat je vazne jen padle na hlavu, nic vic... A ja si vzdycky rikam jaky lidi pisou takovy debilni kody... tak uz mam jeden exemplar :)
    Holt programátor zvyklý pohybovat se na nižších vrstvách pro kterého jsou exoti zase lid ise stylem - paměti máme hromadu a procesory megarychlé.
    Mimochodem můj rekord v tom co by ostatní označili za znepřehledňování kódu byly tři ternární výrazy v sobě (úspora asi dvou instrukcí). Naštěstí jsem si svoje ostruhy už dávno vydobyl (právě v optimalizaci jedné enterpirse aplikace z 20 minut na načtení jedné krizové věci na řádově desítky vteřin - ale tam to bylo o trochu jiných komplexnejíšch změnách), tak teď se bavím otravováním mých mladších kolegů do kterých hustím takovéhle triky.

  11. Citace Původně odeslal TomášX Zobrazit příspěvek
    Prepare lze dát přes foreach, ale již nelze dát ten bind, používají se tam hodnoty z foreach.

    předpokládám, že db je mysql/mariadb.
    Nezkoušel jsem to jenom jsem bral jak to je v dokumentaci hned v prvním příkladu http://php.net/manual/en/mysqli-stmt.execute.php, kde si taky prvně udělá bind_param a až potom si připravuje proměnné. Nicméně jsem netestoval, zda to takto jde i v tom foreach.

  12. Citace Původně odeslal MatesM Zobrazit příspěvek
    Nezkoušel jsem to jenom jsem bral jak to je v dokumentaci hned v prvním příkladu http://php.net/manual/en/mysqli-stmt.execute.php, kde si taky prvně udělá bind_param a až potom si připravuje proměnné. Nicméně jsem netestoval, zda to takto jde i v tom foreach.
    bind_param() bere hodnoty jako reference, až v execute() je obsah proměnných vyhodnocený a přečtený. V tvém odkazovaném případě proběhne v řádku s bind_param inicializace proměnných s NULL hodnotou, o pár řádků níže proběhne přiřazení hodnot do proměnných a poté execute(), který přečte již aktuální hodnoty proměnných. V tomhle případě to teoreticky funguje v pořádku, ale php napříč verzemi mělo spousty bugů v foreach cykles a referencích, je to způsobeno právě tím, že u foreach probíhá při kompilaci několik optimalizací a kód je různě přeformátován (viz performance výsledky níže), viz v googlu klíčová slova "php foreach reference bug". V php 7 se změnila implementace a již by problém s tím být neměly.

    weerwolf:
    Opět ale řešíš problém tam kde není. Máme tady 8000 položek, které se zpracují za 800s, to dělá nějakých 100ms na položku, všechny typy, které jsi sem dal umožní zrychlení o max. jednotky ms. Parsování xml není takhle v php pomalé, načíst vše z db a aktualizovat jen změny může být řešení pokud nebudeš aktualizovat vše nebo většinu (to už je asi lepší přidat podmínku do where na ty stock hodnoty), optimalizovat foreach přinese titěrné zlepšení atd. 100ms odpovídá tomu, že to stojí na db, ať už kvůli její latenci (předpokládám, že db není příliš daleko), tak hlavně kvůli ať už absenci indexů nebo nutnosti ty indexy pravidelně aktualizovat.

    Souhlasím s občasnou potřebnou mikrooptimalizací, ale začít se má od nějnáročnějších částí kódu a postupovat po ty nejméně náročné, tady lítá někde 90ms času pro každou iteraci, zlepšeme nejprve tohle a poté je možné jít dále. Abych věděl, která část je ta náročnější, musím si to změřit, počet vykonaných instrukcí nemusí být určující vzhledem k tomu, že všechny x86 procesory mají out of order pipelinu.

    Rozdíl for vs. foreach jsem ti tady nechal otestovat v několika php verzích https://3v4l.org/QQ4FV. Použil jsem 3 verze, for, for s countem a foreach s 10 000 položkami (viz zdrojáky na odkazu), tj. v podobném rozsahu jako je tady a výsledky jsou následovné:
    - php 7.2.2 - (0.10ms, 0.17ms, 0.13ms)
    - php 5.6.30 - (0.42ms, 2.05ms, 0.52ms)
    - php 5.4.4 - (0.60ms, 2.07ms, 0.62ms)
    - php 5.3.29 - (4.05ms, 2.93ms, 0.76ms)

    Vypadá to, že se honíš za duchy a u nových verzí php (poslední 4 roky) jsou rozdíly zanedbatelné, u php 5.3 dokonce varianta s foreach je rychlejší než s for.

    Mohu se samozřejmě plést, za případnou opravu budu vděčný, pomůže to ostatním, php není můj obor.

  13. Dakujem vam kazdemu za vase komenty. Kedze som laik a samouk, tak ste mi napisali vela podnetnych prispevkov. Chyba bola nakoniec v indexoch databazy. Myslel som si, ze mam indexy nastavene, ale nemal som. Dnes sa mi podarilo index nastavit.

    Prvy krat v zivote som nastavoval index, tak by ste mohli mrknut, ci je nastaveny spravne.

    Imgur: The magic of the Internet
    Imgur: The magic of the Internet

    Moja vzdelavacia otazka este je: Aky je vyznam pola velkost v nastaveni indexu? Ake hodnoty sa tam nastavuju?


    Aktualne casy updatu su priblizne 2 sekundy.

  14. Citace Původně odeslal weerwolf Zobrazit příspěvek
    Holt programátor zvyklý pohybovat se na nižších vrstvách pro kterého jsou exoti zase lid ise stylem - paměti máme hromadu a procesory megarychlé.
    Mimochodem můj rekord v tom co by ostatní označili za znepřehledňování kódu byly tři ternární výrazy v sobě (úspora asi dvou instrukcí). Naštěstí jsem si svoje ostruhy už dávno vydobyl (právě v optimalizaci jedné enterpirse aplikace z 20 minut na načtení jedné krizové věci na řádově desítky vteřin - ale tam to bylo o trochu jiných komplexnejíšch změnách), tak teď se bavím otravováním mých mladších kolegů do kterých hustím takovéhle triky.
    Triky, ktere jen zesloziti ci zneprehledni kod aniz by pridali jakoukoliv znatelnou hodnotu jsou triky k nicemu :) muzete se placat po zadech jak strasne umite premyslet v nizkych urovnich, ale dneska uz je, zvlast pri soucasne slozitosti systémů, mnohem dulezitejsi udrzitelnost kodu. Ve vysledku jde totiz o to kolik usetrite penez a vykon ktery usetrite nepouzivanim foreach nebo naduzivanim dalsich neprehlednych blbin, se kterymi budou kolegove po vas mit problemy pochopit co vubec delaji, rozhodne neobhaji nadnaklady ktere takovym kodem vytvorite pro budouci upravu kodu... Ale chapu, taky sem driv delal v assembleru kde tohle znamenalo vyrazne rozdily... Ale doba je ponekud jinde... dneska jsou dve usetrene instrukce vetsinou jen kapka v mori, ktera proste neznamena nic...

  15. Citace Původně odeslal Creator13 Zobrazit příspěvek
    Dakujem vam kazdemu za vase komenty. Kedze som laik a samouk, tak ste mi napisali vela podnetnych prispevkov. Chyba bola nakoniec v indexoch databazy. Myslel som si, ze mam indexy nastavene, ale nemal som. Dnes sa mi podarilo index nastavit.

    Prvy krat v zivote som nastavoval index, tak by ste mohli mrknut, ci je nastaveny spravne.

    Imgur: The magic of the Internet
    Imgur: The magic of the Internet

    Moja vzdelavacia otazka este je: Aky je vyznam pola velkost v nastaveni indexu? Ake hodnoty sa tam nastavuju?


    Aktualne casy updatu su priblizne 2 sekundy.
    Pěkné, dobrá práce :).

    Index vytvoř jako unikátní - přece nechceš, aby se ti při updatu aktualizovalo více záznamů, každý update by měl odpovídat jednomu. Unikátní index přidá constraint (omezení) a zabrání vložení duplicitních dat.

    Délka indexu je určuje kolik prvních znaků z hodnoty je indexovaných, tj. pokud délku nastavíš na 3 a máš indexovaná slova strop, strana, stráž, index bude obsahovat jedinou položku "str" s odkazem na všechny 3 řádky. Tady pozor, pokud by zároveň takový index byl i unikátní, po vložení slova strop již nebude možné do tabulky vkládat slova jako strana, stráž a další, které začínají na "str".

    První věc co máš špatně je délka pole product_number 255, velikost toho pole by měla odpovídat nejdélší hodnotě, předpokládám, že to budou max. pár desítek znaků a poté i velikost indexu by měla mít shodnou velikost. Změna typu z varchar(255) na třeba varchar(32) je pro teba asi možná příliš složitá, raději nastav velikost indexu na velikost 255 (teď z hlavy nevím, jestli je v mysql podporovaná). Pokud nastavíš unikátní index, pohlídá tě a vyhneš se těmhle peripetiím.

  16. Citace Původně odeslal TomášX Zobrazit příspěvek
    Pěkné, dobrá práce :).

    Index vytvoř jako unikátní - přece nechceš, aby se ti při updatu aktualizovalo více záznamů, každý update by měl odpovídat jednomu. Unikátní index přidá constraint (omezení) a zabrání vložení duplicitních dat.

    Délka indexu je určuje kolik prvních znaků z hodnoty je indexovaných, tj. pokud délku nastavíš na 3 a máš indexovaná slova strop, strana, stráž, index bude obsahovat jedinou položku "str" s odkazem na všechny 3 řádky. Tady pozor, pokud by zároveň takový index byl i unikátní, po vložení slova strop již nebude možné do tabulky vkládat slova jako strana, stráž a další, které začínají na "str".

    První věc co máš špatně je délka pole product_number 255, velikost toho pole by měla odpovídat nejdélší hodnotě, předpokládám, že to budou max. pár desítek znaků a poté i velikost indexu by měla mít shodnou velikost. Změna typu z varchar(255) na třeba varchar(32) je pro teba asi možná příliš složitá, raději nastav velikost indexu na velikost 255 (teď z hlavy nevím, jestli je v mysql podporovaná). Pokud nastavíš unikátní index, pohlídá tě a vyhneš se těmhle peripetiím.
    Diky za rozsirenie mojich obzorov. Zmenil som z varchar(255) na varchar(50) a nastavil som unikatne indexy. Ked som chcel nadstavit velkost na 255, tak mi vyhodilo chybu, tak pravdepodobne nie je v mysql podporovana.

    Este raz dakujem

Hostujeme u Server powered by TELE3