Magento 1: ottimizzazioni delle prestazioni per eliminare entità


10

Attualmente sto cercando di migliorare un paio di moduli per quanto riguarda le prestazioni.

Alcuni di voi potrebbero conoscere l' utilizzo del walk()metodo sulla raccolta, che è molto utile per evitare di scorrere direttamente i prodotti.

Inoltre, grazie a @Vinai, è anche possibile utilizzare il delete()metodo di raccolta .

Ma ho notato che i file nativi di Magento 1 non usano sempre nessuno di questi metodi per la cancellazione.

Uno dei peggiori codici che ho visto è il massDelete()metodo da app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.phpcui i prodotti vengono caricati in un ciclo prima dell'eliminazione .

foreach ($productIds as $productId) {
    $product = Mage::getSingleton('catalog/product')->load($productId);
    Mage::dispatchEvent('catalog_controller_product_delete', array('product' => $product));
    $product->delete();
}

Quindi ho effettuato alcuni test delle prestazioni, ho aggiunto alcune chiamate di registrazione per verificare il tempo impiegato e l'utilizzo della memoria per l'eliminazione di 100 prodotti.

Test 1: walkmetodo

Ho sostituito il codice originale incollato sopra con questo codice:

$collection = Mage::getResourceModel('catalog/product_collection')
                        ->addAttributeToSelect('entity_id')
                        ->addIdFilter($productIds)
                        ->walk('delete');

E i miei risultati sono i seguenti sul mio server di sviluppo scadente (media basata su 10 test):

  • Codice originale: 19,97 secondi, 15,84 MB utilizzati
  • Codice personalizzato: 17,12 secondi, 15,45 MB utilizzati

Quindi, per la cancellazione di 100 prodotti, il mio codice personalizzato è 3 secondi più veloce e usa 0,4 MB in meno.

Test 2: utilizzo del delete()metodo di raccolta

Ho sostituito il codice originale con questo:

$collection = Mage::getResourceModel('catalog/product_collection')
                        ->addAttributeToSelect('entity_id')
                        ->addIdFilter($productIds)
                        ->delete();

E la mente soffiata qui sono i risultati:

  • Codice originale: 19,97 secondi, 15,84 MB utilizzati
  • Codice personalizzato: 1,24 secondi, 6,34 MB utilizzati

Quindi, per la cancellazione di 100 prodotti, il mio codice personalizzato è più veloce di 18 secondi e utilizza 9 MB in meno.

Come indicato nei commenti, sembra che questo metodo non attivi gli eventi Magento (dopo il caricamento, dopo l'eliminazione) né il flush dell'indice / cache.

Domanda

Quindi la mia domanda è: c'è un motivo per cui il core team di Magento non ha usato walk('delete')o evitato meglio il delete()metodo di raccolta invece di caricare i prodotti in un ciclo (che sappiamo tutti è una pessima pratica)?

L'obiettivo principale è essere consapevoli di tali punti chiave in caso di sviluppo di un modulo: ci sono casi particolari in cui non è possibile utilizzare il metodo walk/ collection delete()?

EDIT: il motivo non è sicuramente dovuto al catalog_controller_product_deletefatto che l' evento viene inviato in quanto lo stesso codice può essere trovato in diversi punti (controlla i massDeletemetodi) nel core di Magento. Ho usato l'esempio dei prodotti per evidenziare le prestazioni in quanto di solito sono le entità più grandi


3
Immagino sia a causa dell'evento. Ma sono d'accordo con te, è cattivo stile, in particolare l'uso di getSingleton()come misura delle prestazioni, invece dell'ovvio utilizzo della raccolta. Oh ed è possibile attivare l'evento anche con una raccolta, ma non con la walk()scorciatoia.
Fabian Schmengler,

1
@fschmengler sì, ho pensato anche all'evento, ma come ho detto nella mia modifica, sta accadendo in molti luoghi in cui non viene inviato alcun evento.
Raffaello al Pianismo digitale,

3
Non sorprendente. delete()effettua una query DELETE invece di caricare la raccolta e cancellare ogni prodotto. Con quello perderai davvero gli eventi.
Fabian Schmengler,

5
@fschmengler L'eliminazione di una raccolta comporta anche un'eliminazione per ogni singolo elemento, ma elude la cancellazione della cache e l'attivazione di alcuni eventi di magento e indicizzatore. Ecco da dove proviene la differenza.
Vinai,

2
@Vinai hai ragione. Un
pio desiderio

Risposte:


4

Quindi ho effettuato alcuni test delle prestazioni, ho aggiunto alcune chiamate di registrazione per verificare il tempo impiegato e l'utilizzo della memoria per l'eliminazione di 100 prodotti

Nota a margine, ma dovresti guardare usando il Varien Profiler per questo!

il mio codice personalizzato è più veloce di 2 secondi e usa 0,4 MB in meno

Anche se non ho dubbi sul fatto che la tua modifica migliorerebbe le prestazioni, sarebbe utile fornire i risultati "prima" per confrontare i miglioramenti.

c'è un motivo per cui il core team di Magento non ha utilizzato il walk('delete')ciclo anziché caricare i prodotti in un ciclo (che sappiamo tutti è una pessima pratica)?

Bene, sappiamo da altre domande su questo forum quanto segue:

  • La base di codice Magento si è sviluppata ed evoluta nel corso di molti anni
  • Ci sono stati molti sviluppatori che ci lavorano
  • I processi del flusso di lavoro di sviluppo di base di Magento sono notevolmente migliorati nel tempo in cui hanno lavorato sulla piattaforma, raggiungendo le migliori pratiche e tecniche moderne al punto in cui Magento 2 presenta ora molte pratiche di progettazione di applicazioni moderne all'avanguardia

Quindi suggerirei che l'esempio che hai trovato è probabilmente una delle potenzialmente molte gemme nascoste nel codice che sono state scritte molto tempo fa e / o da uno sviluppatore meno esperto. Come gran parte del codice di base (e del codice di comunità!) Sarebbe stato testato su un piccolo set di dati e non testato in battaglia, quindi le prestazioni potrebbero non essere state attentamente monitorate.

Il tuo miglioramento è vantaggioso e più allineato alle migliori pratiche rispetto al codice originale? Sì. Come sviluppatore della community Magento [1.x], tuttavia, non hai la possibilità di contribuire ai miglioramenti suggeriti, sebbene come fai con Magento 2, quindi il mio suggerimento sarebbe quello di implementarlo in un modulo locale se lo richiedi per le prestazioni in uno dei tuoi negozi o ignoralo se non ti riguarda ma l'hai notato mentre fai delle ricerche.

Come aggiornamento alla tua domanda di modifica, sono sicuro che sei consapevole che il metodo walk in Varien_Data_Collection accetta un callback arbitrario, quindi sarai libero di usarlo per qualsiasi cosa tu abbia voluto probabilmente. Per inviare l'evento nell'esempio originale, è possibile farlo con la funzione walk e con l'eliminazione.

L'unico motivo per cui potrei immaginare che caricare il prodotto prima di eliminarlo possa essere utile potrebbe essere che gli osservatori collegati a quell'evento potrebbero aver bisogno di un set di dati completo non disponibile senza caricare prima il prodotto. In tal caso, spiegherebbe perché usano un singleton piuttosto che un modello per minimizzare almeno le spese generali dell'oggetto.


Grazie ho aggiunto i risultati prima e dopo al post. Quindi pensi che non ci sia un motivo particolare a parte il fatto che è il vecchio codice?
Raffaello al Pianismo digitale,

2
Sarebbe la mia ipotesi, sì. Il caricamento del prodotto prima dell'eliminazione non avrebbe alcun vantaggio se non quello di generare eventi di caricamento, che non sono rilevanti per l'eliminazione. Normalmente carichi un prodotto per ottenere il suo set completo di dati, che potrebbe essere richiesto per uno degli osservatori collegati all'evento - in tal caso spiegherebbe perché stanno usando un singleton invece del modello.
Robbie Averill,

1
Vedi la mia modifica con più test, i risultati sono ancora più folli
Raphael al Digital Pianism,

0

catalog_controller_product_deletePenso che lo stiano facendo per lanciare l' evento che viene utilizzato da Mage_Tag.

catalog_product_delete_beforeo catalog_product_delete_aftersignificherebbe che non era necessario, anche se avrei pensato. Mi chiedo se questo particolare evento viene utilizzato anche per la registrazione delle azioni dell'amministratore.


massDelete()CustomerController.php
Ci ho pensato anche io,

Vedi la mia modifica con più test, i risultati sono ancora più folli
Raphael al Digital Pianism,

0

Penso che l'eliminazione di massa dovrebbe funzionare come eliminare un singolo prodotto (completamente caricato).

Per $collection->delete()la risposta è già stata data. Se non si attiva deleter_before, delete_afterpotrei eventualmente interrompere alcune estensioni e ignorare alcuni osservatori utilizzati nel core.

$collection->walk('delete')potrebbe funzionare, ma presenta comunque lo svantaggio che i dati del prodotto non sono completi. Questo può anche rompere gli osservatori personalizzati se si basano su dati aggiuntivi, ad esempio oggetto articolo di scorta.

Credo che, se si cambia ->addAttributeToSelect('entity_id')a ->addAttributeToSelect('*')e aggiungere ->setFlag('require_stock_items', true)(per aggiungere i dati di stock a prodotti) non avrà un rendimento migliore allora "loop-delete".

Sembra un cattivo stile, ma penso che sia giusto per entrambe le azioni di eliminazione di massa.

Uso walk()e anche delete()per modelli personalizzati, ma so che non ci sono osservatori o entity_idè abbastanza. Solo per citare, walk()funzionerebbe con tutti gli eventi utilizzati nel core, perché usano solo $product->getId(), ma non si conoscono osservatori di terze parti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.