Inserimenti/Aggiornamenti Batch

Buongiorno a tutti,

sono un felice utilizzatore di codeigniter, ma che sta valutando di passare a yii2 per l’implementazione dei nuovi progetti.

In Codeigniter ero abituato ad usare un paio di funzioni utili: gli inserimenti e modifiche batch.

Queste due funzioni erano davvero comode quando avevo a che fare con grandi quantità di dati.

Ora mi trovo a leggere da un file circa 20.000 righe, le quali devono essere inserite nel database oppure semplicemente aggiornate.

Il problema che mi si pone con yii2 è: il tempo impiegato è davvero lungo ed i 128MB di ram assegnati al processo PHP purtroppo non bastano per eseguire l’intero comando.

Vi allego un esempio di codice che sto usando per l’aggiornamento:




$connection = \Yii::$app->db;

		$transaction = $connection->beginTransaction();

		try {

			foreach($result as $key=>$value){

				$sql = $connection->createCommand()->update('product', [

				'codice_articolo' => $value['codice_articolo'],

				'descrizione' => $value['descrizione'],

				'descrizione_estesa' => $value['descrizione_estesa'],

				'unita_di_misura' => $value['unita_di_misura'],

				'prezzo_lordo' => $value['prezzo_lordo'],

				'sconto1' => $value['sconto1'],

				'sconto2' => $value['sconto2'],

				'sconto3' => $value['sconto3'],

				'prezzo_netto' => $value['prezzo_netto'],

				'barcode' => $value['barcode'],

				'categoria_livello1' => $value['categoria_livello1'],

				'categoria_livello2' => $value['categoria_livello2'],

				'descrizione_cat_1' => $value['descrizione_categoria_livello1'],

				'descrizione_cat_2' => $value['descrizione_categoria_livello2'],

				],

				['codice_articolo' => $value['codice_articolo']])->execute();

			}

			$transaction->commit();

		} catch (Exception $e) {

			$transaction->rollBack();

		}



Sapete dirmi come potrei ottimizzare il codice? Come fate voi in questi casi?

Ciao e benvenuto!

Per fare grossi import ti conviene separare l’import in blocchi di N righe e non in un’unica transazione altrimenti, con un foreach di tutto, php si deve mettere in memoria tutte le 20k righe e ovviamente l’uso delle risorse diventa elevato.

Puoi provare diverse vie, una è caricare il file nel server e importarle con il comando mysql "LOAD DATA LOCAL INFILE", questo può essere buono se esegui il comando via CLI e non da una pagina web.

La seconda soluzione è un po’ più complicata e, per evitare che php vada in timeout, dovresti ricaricare la pagina passando ogni volta il punto in cui sei arrivato.

Esempio, decidi che N è il tuo numero di righe da importare ad ogni ciclo.

carichi il file e leggi il numero totale delle righe (così sai ogni volta a che punto stai), carichi le prime N righe in memoria e fai l’import, poi fai un refresh della pagina passando il numero di righe che hai importato (sarà il tuo offset), al caricamento importi le righe da offset a offset + N e così via…

E’ la via più complicata ma ti permette di importare un numero infinito di righe perché ad ogni ciclo l’uso delle risorse è limitato al valore di N :)

facci sapere!

Per eseperienza personale, dovendo lavorare anche con milioni di record talvolta, i batch li eseguo avviando la transaction come fai te, ma poi eseguo del SQL puro contro il db, questo ha due benefici

  • risparmia tantissima memoria perchè PHP non deve manovrave migliaia di volte con gli oggetti di yii

  • risparmia tantissimissimo tempo, per lo stesso motivo

Prova

Se non splittate vi consiglio "LOAD DATA INFILE" su MySQL che è molto più veloce di una insert, circa 20 volte più veloco secondo oracle ed è buona cosa, sempre per le prestazioni, disabilitare il controllo delle chiavi straniere quando si importa.

Gentilissimi…

Proverò le soluzioni che mi avete suggerito e poi vi farò sapere com’è andata.

Al momento funziona con l’avvio della transazione, ma si può sempre migliorare.