Il reindex completo di Maho ora va da tre a cinque volte più veloce
La pull request #1582 è unita: statistiche del planner su SQLite, corsie parallele su MySQL e PostgreSQL, scritture per differenza. Sullo store di esempio si passa da 5,2 a 0,95 secondi.
Un ./maho index:reindex:all sullo store di dati di esempio, 1.040 prodotti per 15 store view,
prima impiegava 5,2 secondi su SQLite e adesso ne impiega 0,95. È il tipo di guadagno che non si
nota finché non si guarda il numero, e che poi si sente ogni volta che si aggiorna un catalogo. La
pull request #1582
di fballiano è stata unita
il 19 ottobre 2026 e arriverà con la 26.11.0.
Cosa è cambiato
Il reindex completo è più veloce su tutti e tre i motori supportati:
| Motore | Prima | Dopo |
|---|---|---|
| SQLite | 5,2 s | 0,95 s |
| MySQL | 4,54 s | 1,63 s |
| PostgreSQL | 5,58 s | 1,09 s |
Sono misure di una sola esecuzione per ramo, fatte dall’autore della pull request sullo store di esempio: un catalogo grande non è stato cronometrato. Il dato che regge meglio del tempo è il numero di query. Una seconda ricostruzione completa degli URL rewrite manda 545 query invece di 17.079, e l’indice di ricerca 199 invece di 1.296. Le tabelle degli indici contengono le stesse righe di prima.
Le quattro leve
Le statistiche del planner su SQLite. Dopo un reindex completo, Mage_Index_Model_Process::reindexAll()
chiama il nuovo AdapterInterface::optimizeStatistics(): PRAGMA optimize su SQLite, ANALYZE delle
tabelle indicate su PostgreSQL, niente su MySQL. Senza statistiche, SQLite sceglieva per l’inserimento
nella tabella di relazione EAV un piano che leggeva circa 17 milioni di righe: quella sola istruzione
era 3,2 dei 3,4 secondi di catalog_product_attribute. Il filtro sulle giacenze degli indici
(Stock_Status::prepareCatalogProductIndexSelect()) usa ora un EXISTS invece di una join, e questo
sistema il piano anche senza statistiche.
Una sola esecuzione per indice. index:reindex:all e l’installer dei dati di esempio scorrono
Runner::buildQueue(). Il ciclo vecchio creava una nuova collezione di processi, mentre
reindexEverything() risolve le dipendenze attraverso il singleton degli indici, quindi il flag
“già fatto” finiva su un altro oggetto. Ora catalogsearch_fulltext dichiara di dipendere da
cataloginventory_stock, visto che la ricostruzione della ricerca legge lo stato delle giacenze.
Le corsie parallele. Runner::buildLanes() divide la coda in gruppi di processi che dipendono
gli uni dagli altri. Su MySQL e PostgreSQL il comando avvia un processo figlio per ogni corsia, e
oggi le corsie sono quattro: giacenze, attributi, prezzi e ricerca insieme, poi URL, poi categorie,
poi tag. SQLite ha un solo scrittore, quindi resta in serie, e --sequential forza la serie
ovunque:
# un indice alla volta, anche su MySQL e PostgreSQL
./maho index:reindex:all --sequential
Le scritture per differenza. Mage_Index_Model_Resource_Abstract::syncData() cancella dalla
tabella principale le righe che non hanno corrispondenza in _idx, poi inserisce o aggiorna solo le
righe nuove e quelle in cui una colonna cambia, confrontate con il nuovo getNullSafeEqualsSql(). Un
reindex completo di un catalogo che non è cambiato ora scrive quasi nulla nelle tabelle principali.
Il resto del lavoro
- gli URL rewrite:
saveRewrite()salta una riga che non è cambiata, erefreshProductsRewrites()reindicizza molti prodotti con un gruppo di query ogni 250 prodotti; - gli eventi in sospeso:
Process::_processEventsCollection()raccoglie fino a 500 eventi accettati dall’indexer e li reindicizza con una sola chiamata aprocessBatch(), che passa dareindexEntity(). Prezzi, EAV, giacenze, categorie, ricerca e URL dichiaranogetBatchEntityIds(). Un evento che non può entrare in un batch, come una cancellazione o un cambio di categoria, gira da solo; - l’indice di ricerca: la ricostruzione legge i figli di un intero batch di prodotti, con la nuova
impostazione
catalog/search/index_batch_size, 500 invece del 100 fisso di prima. Nel pannello è in Catalog > Catalog Search > Products per Search Index Batch; - lo schema: le tabelle
_idxe_tmpdi prezzi ed EAV perdono gli indici a colonna singola che nessuna query usa, ecatalog_product_entityguadagna un indice su(type_id, entity_id); - la griglia degli indici in System > Index Management e l’API degli indici chiedono
hasUnprocessedEvents()per il flag “Update Required”, invece di caricare tutti gli eventi in sospeso solo per contarli.
I bug che la pull request si porta dietro
Il lavoro sulle prestazioni ha fatto emergere cinque difetti:
- dopo un aggiornamento massivo dei prodotti, l’indice dei prezzi poteva perdere i prezzi a scaglioni e quelli per gruppo dei prodotti aggiornati e di altri prodotti;
- su PostgreSQL e SQLite, un prodotto non visibile in uno store faceva sparire l’indice della navigazione a livelli di tutti gli store;
./maho index:reindex:productnon faceva niente per gli indici di ricerca, categorie e tag;index:reindex:allricostruiva l’indice delle giacenze due volte;- su MySQL, due checkout di due figli dello stesso prodotto bundle potevano fallire con un deadlock nell’indice delle giacenze.
Il primo è quello che pesa di più in produzione: un prezzo a scaglioni che sparisce dall’indice non dà errore, dà un prezzo sbagliato a chi compra.
Cosa cambia per script e codice custom
Tre punti da controllare prima di aggiornare:
index:reindex:allstampa il risultato di ogni indice quando la sua corsia finisce, non nell’ordine dei codici. Uno script che legge l’output riga per riga deve tenerne conto, oppure passare--sequential;- il comando non si ferma più al primo indice che fallisce: esegue gli altri e esce con un codice di errore. Un deploy che si fidava della prima riga di errore deve guardare il codice di uscita;
- il reindex completo dei prezzi tiene il lock della tabella
_tmpdall’inizio alla fine. Un salvataggio prodotto in modalità realtime, o un checkout che cambia uno stato di giacenza, aspetta che il reindex finisca.
Un indexer di un tipo di prezzo scritto da terzi che si aspetta la vecchia transazione esterna
continua a funzionare: la sua beginTransaction() annidata ora apre una transazione vera.
Quando arriva
La pull request è unita in main il 19 ottobre 2026 e la milestone della 26.11.0 era ancora aperta
alla data di questo articolo, quindi il codice arriva con quella release. Il reindex non è un
dettaglio da amministrazione: è il passaggio che un negozio paga a ogni import, a ogni aggiornamento
di massa e a ogni apertura di uno store nuovo.
Sui tempi di un reindex non si vince con un colpo solo. Si vince togliendo le query che nessuno aveva chiesto.
Per il quadro della piattaforma, con le novità che le release portano avanti, la panoramica è nella pagina Scopri MahoCommerce.
Nota di redazione.Testo preparato con l'aiuto dell'AI e verificato sulla fonte prima della pubblicazione. Può contenere errori: se ne trovi uno, segnalalo.Leggi il Manifesto
Fonte: MahoCommerce/maho#1582
Il tuo negozio vende ancora?
Raccontaci da dove parti: ti rispondiamo con una prima valutazione, senza impegno.
Chiedi l'analisi del negozio