{"id":20930,"date":"2026-08-23T15:05:18","date_gmt":"2026-08-23T13:05:18","guid":{"rendered":"https:\/\/webhosting.de\/mariadb-query-optimizer-intern-erklaert-sql-tuning-insight\/"},"modified":"2026-08-23T15:05:18","modified_gmt":"2026-08-23T13:05:18","slug":"spiegazione-interna-dellottimizzatore-di-query-di-mariadb-approfondimenti-sullottimizzazione-sql","status":"publish","type":"post","link":"https:\/\/webhosting.de\/it\/mariadb-query-optimizer-intern-erklaert-sql-tuning-insight\/","title":{"rendered":"Spiegazione interna dell\u2019ottimizzatore di query di MariaDB: nozioni di base, piani e applicazione pratica"},"content":{"rendered":"<p>Spiego il <strong>Ottimizzatore MariaDB<\/strong> Esempi pratici: come crea i piani, stima i costi e perch\u00e9 a volte sbaglia. Ecco come leggere il piano di esecuzione SQL in modo mirato, utilizzare gli indici in modo sensato e guidare l'ottimizzatore con i fatti anzich\u00e9 con l'istinto.<\/p>\n\n<h2>Punti centrali<\/h2>\n\n<p>Per cominciare, riassumo brevemente gli elementi fondamentali, in modo che tu possa contestualizzare in modo mirato le sezioni seguenti e il <strong>Panoramica<\/strong> mantieni.<\/p>\n<ul>\n  <li><strong>Fasi<\/strong>: L'analisi, la preparazione, l'ottimizzazione e l'esecuzione costituiscono il ciclo di vita di ogni query.<\/li>\n  <li><strong>Modello di costo<\/strong>: I valori in microsecondi basati sul tempo determinano la selezione degli indici, le scansioni e l'ordine dei join.<\/li>\n  <li><strong>Statistiche<\/strong>: La cardinalit\u00e0 e gli istogrammi determinano la stima della selettivit\u00e0.<\/li>\n  <li><strong>Trasparenza<\/strong>: EXPLAIN, EXPLAIN ANALYZE e Optimizer Trace aprono la \u201cscatola nera\u201d.<\/li>\n  <li><strong>Sintonizzazione<\/strong>: Gli indici, la riscrittura delle query, il comando ANALYZE TABLE e i parametri di costo aumentano la velocit\u00e0.<\/li>\n<\/ul>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/mariaDB-query-plans-9842.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Ciclo di vita di una query in MariaDB<\/h2>\n\n<p>Prima che venga generato un piano, una query passa attraverso quattro fasi che verifico in modo mirato nella pratica quotidiana per <strong>Cause<\/strong> da individuare come causa di lentezza. Durante l\u2019analisi sintattica, MariaDB converte l\u2019SQL in una struttura interna; \u00e8 qui che emergono gli errori di sintassi. Nella fase di preparazione, il motore verifica tabelle, colonne e potenziali indici ed esegue semplici trasformazioni. Segue quindi la fase di ottimizzazione, in cui vengono calcolati i piani candidati e valutati con un modello di costo. Nella fase di esecuzione, il server attua il piano selezionato passo dopo passo: lettura, join, filtraggio, restituzione.<\/p>\n\n<p>Distinguo chiaramente gli errori di analisi in base alla fase, perch\u00e9 in questo modo le diagnosi hanno effetto pi\u00f9 rapidamente e <strong>Misure<\/strong> agire in modo mirato. Nella maggior parte dei casi, i problemi di prestazioni hanno origine nell\u2019ottimizzazione: stime errate, indici mancanti o sequenze di join sfavorevoli. Gli errori di parsing sono banali, ma la fase di preparazione pu\u00f2 gi\u00e0 comportare alcune sottigliezze, come la risoluzione delle view o la riorganizzazione delle sottoquery. Nella fase di esecuzione, le inefficienze saltano all\u2019occhio senza piet\u00e0 se in precedenza \u00e8 stata scelta una scansione completa. Per questo motivo, inizio ogni analisi con una panoramica strutturata di tutte e quattro le fasi.<\/p>\n\n<h2>Come funziona il processo decisionale interno dell\u2019Optimizer<\/h2>\n\n<p>MariaDB opera in base ai costi e valuta le esecuzioni alternative tramite un <strong>Funzione di costo<\/strong>. Per ogni variante, il server stima il numero di righe lette, la selettivit\u00e0 delle condizioni WHERE\/ON, i tipi di accesso quali Table Scan, Index Scan e Range Scan, nonch\u00e9 il tempo richiesto per le singole operazioni. Internamente, il server distingue tra join_preparation e join_optimization. In join_preparation vengono eseguite la riscrittura delle query, la semplificazione delle condizioni, la trasformazione delle sottoquery e la risoluzione delle viste. La fase `join_optimization` calcola gli ordini di join, verifica i candidati all\u2019indicizzazione tramite `ref_optimizer_key_uses`, stima il numero di righe tramite `Range Scan` e assegna le condizioni a tabelle specifiche il prima possibile.<\/p>\n\n<p>Questo meccanismo spiega perch\u00e9 un piccolo filtro posizionato nel posto sbagliato pu\u00f2 causare costosi <strong>Conseguenze<\/strong> ha. Se l\u2019operazione `attaching_conditions_to_tables` avviene in una fase tardiva, il piano trascina un numero inutilmente elevato di righe attraverso i join. Se le statistiche sono obsolete, `rows_estimation` e `Selectivity` risultano errati; l\u2019ottimizzatore ricorre quindi a percorsi di accesso apparentemente vantaggiosi, ma in realt\u00e0 lenti. \u00c8 proprio su questi aspetti che intervengo: statistiche migliori, predicati pi\u00f9 chiari, indici compositi ordinati in modo accurato. In seguito, la scelta del piano spesso cambia in modo tangibile.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/MariaDBQueryOptKonferenz1234.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Modello dei costi a partire da MariaDB 11.0<\/h2>\n\n<p>Le versioni attuali non valutano pi\u00f9 il lavoro in modo approssimativo in base ai pesi, ma con <strong>microsecondi<\/strong> per operazioni di archiviazione specifiche. Parametri quali optimizer_disk_read_cost, optimizer_disk_read_ratio e optimizer_where_cost avvicinano il modello ai tempi di esecuzione reali. In questo modo, l\u2019ottimizzatore confronta la scansione dell\u2019intervallo dell\u2019indice con la scansione completa sulla base di ipotesi temporali reali. LAST_QUERY_COST mostra i costi totali stimati e spesso presenta una correlazione con la realt\u00e0 nettamente migliore rispetto al passato. Per i sistemi con grandi volumi di dati, questa maggiore precisione offre vantaggi immediati.<\/p>\n\n<p>Calibro il modello con attenzione quando le caratteristiche hardware contraddicono le ipotesi predefinite e quindi la <strong>Scelta del piano<\/strong> distorcere. Gli SSD NVMe, le memorie distribuite o le cache specializzate possono modificare in modo significativo il rapporto disco e i tempi di lettura. Piccole modifiche ai valori di `optimizer_costs` fanno s\u00ec che MariaDB privilegi percorsi pi\u00f9 efficienti. Documento ogni modifica e poi controllo EXPLAIN ANALYZE per misurarne l'effetto. Senza misurazioni, l'ottimizzazione rimane un terno al lotto.<\/p>\n\n<h2>Selettivit\u00e0, statistiche e istogrammi<\/h2>\n\n<p>Le stime accurate partono da dati precisi <strong>cardinalit\u00e0<\/strong> e di una selettivit\u00e0 affidabile. MariaDB tiene traccia delle statistiche relative ai diversi valori per ogni colonna e, facoltativamente, pu\u00f2 utilizzare istogrammi per le distribuzioni. Proprio i dati non uniformi \u2013 hotspot, distribuzioni di Zipf, modelli stagionali \u2013 traggono vantaggio dagli istogrammi. Dopo modifiche significative ai dati, eseguo ANALYZE TABLE affinch\u00e9 l\u2019ottimizzazione possa nuovamente basarsi su dati effettivi. Chi se ne dimentica rischia scansioni complete che sono oggettivamente errate.<\/p>\n\n<p>Programmo ANALYZE come processo periodico, in base a <strong>Cambiamenti<\/strong> nel volume dei dati e sulle tabelle critiche. In caso di distribuzioni delle colonne fortemente distorte, gli istogrammi aiutano a valutare in modo realistico la selettivit\u00e0 dei valori singolari. Ci\u00f2 riduce gli errori di valutazione nelle scansioni di intervallo e nelle strategie di unione. In combinazione con indici compositi adeguati, la precisione dei risultati migliora notevolmente. Risultato: tempi di esecuzione pi\u00f9 brevi e meno operazioni di I\/O.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/mariadb-query-optimizer-guide-4729.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Lettura di EXPLAIN e dei piani di esecuzione<\/h2>\n\n<p>Per rendere visibili le decisioni, utilizzo EXPLAIN, EXPLAIN EXTENDED e <strong>FORMATO=JSON<\/strong>. Le colonne classiche offrono una rapida panoramica: id, select_type, table, type, possible_keys, key, key_len, ref, rows ed eventualmente filtered. Un valore type=ALL indica una scansione completa, che raramente \u00e8 auspicabile. FORMAT=JSON mostra in dettaglio come sono state spostate le condizioni e quali percorsi sono stati valutati dall\u2019ottimizzatore. Nel contesto dell\u2019hosting, consiglio la guida su <a href=\"https:\/\/webhosting.de\/it\/piani-di-esecuzione-delle-query-di-database-che-ospitano-informazioni-sulle-prestazioni-di-ottimizzazione\/\">Piani di esecuzione nell'hosting<\/a>, per collegare le informazioni relative ai piani agli effetti sulle infrastrutture.<\/p>\n\n<p>Per una rapida interpretazione mi \u00e8 d\u2019aiuto una piccola tabella che riassume brevemente i valori tipici e quindi <strong>Interpretazioni errate<\/strong> impedito.<\/p>\n\n<table>\n  <thead>\n    <tr>\n      <th>Campo EXPLAIN<\/th>\n      <th>Valore tipico<\/th>\n      <th>Significato nella pratica<\/th>\n    <\/tr>\n  <\/thead>\n  <tbody>\n    <tr>\n      <td>tipo<\/td>\n      <td>ALL, range, ref, eq_ref, const<\/td>\n      <td>Pi\u00f9 ci si sposta verso destra, pi\u00f9 la selezione \u00e8 rigorosa; ALL indica una scansione completa.<\/td>\n    <\/tr>\n    <tr>\n      <td>possible_keys<\/td>\n      <td>Elenco degli indici<\/td>\n      <td>Indici che, in teoria, sono adeguati; se qui mancano dei candidati, manca la struttura.<\/td>\n    <\/tr>\n    <tr>\n      <td>chiave<\/td>\n      <td>Nome dell'indice<\/td>\n      <td>Indice effettivamente utilizzato; se vuoto, significa che non viene utilizzato alcun indice.<\/td>\n    <\/tr>\n    <tr>\n      <td>righe<\/td>\n      <td>Numero<\/td>\n      <td>Stima delle righe lette; forte discrepanza rispetto alla realt\u00e0 = statistica inaffidabile.<\/td>\n    <\/tr>\n    <tr>\n      <td>filtrato<\/td>\n      <td>Per cento<\/td>\n      <td>Quanto viene trasmesso dopo il filtro; spesso \u00e8 meglio che sia poco.<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n\n<h2>Perch\u00e9 a volte l\u2019Optimizer sbaglia<\/h2>\n\n<p>Non esiste un modello di costi che si adatti a ogni situazione, quindi lo correggo <strong>Errori<\/strong> in modo mirato. Le statistiche obsolete portano a stime errate delle righe e a sequenze di join sfavorevoli. Indici compositi strutturati in modo errato impediscono l\u2019utilizzo degli indici nei filtri a pi\u00f9 colonne. Sottoquery molto annidate rendono difficili le riscritture efficaci e bloccano la materializzazione. Filtri mancanti o fuorvianti costringono il motore a spostare molte righe prima che i predicati utili abbiano effetto.<\/p>\n\n<p>Per prima cosa verifico se la formulazione della query \u00e8 conforme al <strong>Indice<\/strong> Ci\u00f2 che funziona davvero: regola del prefisso a sinistra, ordine di ordinamento adeguato, evitare funzioni sulle colonne nella clausola WHERE. Dopodich\u00e9 controllo in EXPLAIN ANALYZE se la realt\u00e0 conferma la stima. In caso contrario, eseguo ANALYZE TABLE e, se necessario, una riscrittura. Solo come ultima risorsa ricorro a FORCE INDEX o all\u2019hinting, poich\u00e9 ci\u00f2 pu\u00f2 limitare le ottimizzazioni future.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/mariadboptimizer_2219.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Utilizzo mirato della traccia dell'ottimizzatore<\/h2>\n\n<p>Se EXPLAIN non \u00e8 sufficiente, attivo l'Optimizer Trace e monitoro <strong>Decisioni<\/strong> nel log JSON. Qui posso vedere quali piani sono stati presi in considerazione, scartati o accettati. Capisco perch\u00e9 una condizione si applica in ritardo o perch\u00e9 un indice non \u00e8 stato preso in considerazione. Il log mostra anche come sono state riorganizzate le condizioni. Questa visione approfondisce la comprensione e fornisce leve concrete per la prossima fase di ottimizzazione.<\/p>\n\n<p>Salvo le sezioni rilevanti della traccia insieme all'hash della query e <strong>Parametri<\/strong>valutare. In questo modo, in seguito potr\u00f2 confrontare quale modifica abbia prodotto quale effetto. La documentazione del server MariaDB e diverse presentazioni nell\u2019ecosistema descrivono i campi in modo dettagliato (fonte: documentazione del server MariaDB su Query Optimizer e Optimizer Trace). Con questo strumento riesco a individuare le ipotesi errate pi\u00f9 rapidamente rispetto al metodo per tentativi ed errori. Risparmio tempo soprattutto nei join complessi.<\/p>\n\n<h2>Esercitazione pratica: ottimizzazione del database passo dopo passo<\/h2>\n\n<p>Inizio ogni ottimizzazione con una chiara <strong>Misurazione<\/strong>. Individuo i problemi tramite il monitoraggio e il <a href=\"https:\/\/webhosting.de\/it\/mysql-lento-registro-delle-query-hosting-analizzare-queryperf\/\">Registro delle query lente<\/a>. Successivamente confronto EXPLAIN con EXPLAIN ANALYZE per mettere a confronto il piano e i risultati effettivi. Adatto la strategia di indicizzazione a WHERE, JOIN e ORDER BY; allineo gli indici compositi ai punti di accesso pi\u00f9 frequenti. Utilizzo FORCE INDEX solo se l\u2019ottimizzatore, nonostante statistiche corrette, seleziona il candidato sbagliato.<\/p>\n\n<p>Ogni fase prevede la cura della <strong>Statistiche<\/strong>: ANALYZE TABLE su tabelle molto trafficate, istogrammi per distribuzioni asimmetriche. Semplifico le sottoquery superflue, materializzo i risultati intermedi se necessario ed elimino le vecchie soluzioni provvisorie. In presenza di hardware speciale, verifico gli optimizer_costs affinch\u00e9 il modello a microsecondi sia corretto. Documento ogni modifica con valori \u00abprima\u00bb e \u00abdopo\u00bb, in modo che l\u2019effetto rimanga tracciabile nel tempo.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/mariadb_query_optimizer_8390.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Problemi tipici degli ottimizzatori e relative soluzioni<\/h2>\n\n<p>Se EXPLAIN type=ALL, nonostante il campo possible_keys sia pieno, controllo innanzitutto <strong>Selettivit\u00e0<\/strong>. Spesso l'ordine delle colonne nell'indice composito non \u00e8 corretto oppure una funzione impedisce l'utilizzo dell'indice. In questi casi inverto l\u2019ordine, rimuovo le funzioni che causano problemi o suddivido i predicati. Se l\u2019ordine dei join \u00e8 errato, verifico se sia possibile applicare un filtraggio iniziale, ad esempio anteponendo la tabella pi\u00f9 selettiva. Laddove opportuno, converto le sottoquery in join o tabelle TEMPORARY.<\/p>\n\n<p>Riconosco le decisioni errate anche da valori fortemente discostanti <strong>righe<\/strong> tra teoria e realt\u00e0. In questi casi \u00e8 utile ricorrere al comando ANALYZE TABLE o a un istogramma sulla colonna in questione. Se anche statistiche corrette non portano al risultato desiderato, prendo in considerazione l\u2019uso di hint espliciti. Prima di farlo, salvo i dati di controllo e i valori misurati, in modo che le versioni successive dell\u2019ottimizzatore non vengano rallentate a causa di impostazioni memorizzate. In questo caso, la disciplina nella documentazione ripaga.<\/p>\n\n<h2>Contesto dell'hosting e aspetti operativi<\/h2>\n\n<p>La qualit\u00e0 delle query e l'infrastruttura devono essere in sintonia, altrimenti l'applicazione va sprecata <strong>Potenziale<\/strong>. SSD veloci, cache coerenti e una configurazione ottimale sono la base su cui l\u2019Optimizer prende le decisioni giuste. Un traffico elevato non tollera scansioni complete; poche query errate rallentano interi sistemi. Per gli ambienti MySQL\/MariaDB in produzione, forniscono consigli pratici come <a href=\"https:\/\/webhosting.de\/it\/mysql-optimizer-query-hosting-ottimizzazione-serverboost\/\">Ottimizzatore MySQL<\/a> spunti di riflessione utili sulla combinazione tra piano e piattaforma. Chi tiene conto di questo aspetto previene i colli di bottiglia prima che si aggravino.<\/p>\n\n<p>Associ sempre l'analisi del piano alle metriche relative a <strong>I\/O<\/strong>, latenza e concorrenza. Se i valori non corrispondono al modello di costi ipotizzato, verifico i parametri. Successivamente, esamino le dimensioni dei buffer, i carichi di lavoro paralleli e la distribuzione degli hotset. In questo modo riesco a gestire in modo armonioso le query e le risorse e a mantenere sotto controllo i picchi di carico.<\/p>\n\n<h2>Percorsi di join e di accesso nella pratica<\/h2>\n\n<p>Chiaro molti malintesi spiegando che il <strong>Tipi di accesso<\/strong> valutino in modo mirato l'uno rispetto all'altro. Un <em>intervallo<\/em>- o <em>rif.<\/em>-L'accesso funziona quasi sempre <em>TUTTO<\/em>. In caso di collegamenti con condizioni di uguaglianza su chiavi univoche (<em>eq_ref<\/em>) i progetti sono particolarmente solidi. Verifico inoltre se un <strong>Indice di copertura<\/strong> che soddisfa completamente la query: se tutte le colonne necessarie sono presenti nell'indice, MariaDB evita costosi accessi alle tabelle. <strong>Condizione Index Pushdown (ICP)<\/strong> aiuta a verificare condizioni WHERE aggiuntive gi\u00e0 nell'indice \u2013 ci\u00f2 riduce il numero di righe restituite e le operazioni di I\/O.<\/p>\n\n<p>Circa <strong>Unione di indici<\/strong> MariaDB pu\u00f2 combinare pi\u00f9 indici (intersezione\/unione). Ci\u00f2 \u00e8 utile in presenza di predicati OR o di pi\u00f9 condizioni selettive, ma spesso risulta pi\u00f9 lento rispetto a un indice composito ben scelto. Sto inoltre valutando <strong>MRR<\/strong> (Lettura multigamma) e <strong>BKA<\/strong> (Batched Key Access). MRR ordina le chiavi primarie da leggere per livellare gli I\/O casuali; BKA raggruppa le ricerche nei join e offre vantaggi soprattutto nei join non coperti. In pratica, testo BKA\/MRR tramite optimizer_switch e verifico con EXPLAIN ANALYZE se i modelli di I\/O diminuiscono. Se invece MariaDB ricorre al <strong>Ciclo annidato a blocchi<\/strong> (BNL), di solito \u00e8 pi\u00f9 vantaggioso aumentare la dimensione del buffer di join (join_buffer_size) \u2013 oppure utilizzare un rewrite che consenta dei veri e propri join su indici.<\/p>\n\n<pre><code>-- Esempio: indice composito per join + filtro + ordinamento\nCREATE INDEX ix_orders_cust_status_created\n  ON orders (customer_id, status, created_at);\n\n-- Accesso tipico\nSELECT *\nFROM orders o\nJOIN customers c ON c.id = o.customer_id\nWHERE o.status = 'open' AND o.created_at &gt;= '2026-01-01'\nORDER BY o.created_at DESC\nLIMIT 50;\n<\/code><\/pre>\n\n<p>Grazie all'indice sopra indicato, l'ottimizzatore pu\u00f2 scegliere l'ordine pi\u00f9 selettivo, valutare i filtri in anticipo e spesso eseguire l'ordinamento senza ricorrere a un filesort aggiuntivo.<\/p>\n\n<h2>ORDER BY, GROUP BY, Filesort e tabelle temporanee<\/h2>\n\n<p>L'ordinamento e l'aggregazione richiedono tempo. Mi assicuro che <strong>ORDER BY<\/strong> e <strong>GRUPPO PER<\/strong> possono essere eseguiti nell'ordine dell'indice. Ci\u00f2 funziona se il prefisso e la direzione corrispondono esattamente. Altrimenti viene applicata una <strong>Filesort<\/strong> con buffer di ordinamento (sort_buffer_size) ed eventualmente una tabella temporanea. Se l'insieme dei risultati contiene colonne TEXT\/BLOB di grandi dimensioni, MariaDB risulta pi\u00f9 veloce <em>su disco<\/em> Tabelle TEMP (Aria). Adotto misure preventive selezionando solo le colonne necessarie, caricando i campi di grandi dimensioni solo alla fine oppure utilizzando prefissi di lunghezza limitata.<\/p>\n\n<p>Nelle aggregazioni, quando possibile, utilizzo, <strong>Scansione dell'indice non ottimizzata<\/strong> (ad es. GROUP BY sulla parte principale dell'indice) e seleziono indici compositi lungo il raggruppamento. Quando i risultati intermedi diventano voluminosi, una materializzazione con chiavi appropriate offre una scalabilit\u00e0 migliore rispetto a un unico mega-join. Misuro regolarmente le metriche degli handler e i contatori Created_tmp_* per individuare i punti critici relativi all'ordinamento e alle tabelle temporanee.<\/p>\n\n<h2>Sottoconsultazioni, semi-join e materializzazione<\/h2>\n\n<p>Molte sottoquery possono essere riformulate in modo efficiente in fase di preparazione. Le strutture IN\/EXISTS possono essere utilizzate come <strong>Semi-join<\/strong> funzionano, con strategie come Materialization o LooseScan. Verifico se l\u2019Optimizer \u00e8 un <strong>derived_merge<\/strong> \u00e8 stato possibile: se una tabella derivata (o un CTE WITH) viene inserita nel piano esterno, i suoi indici sono immediatamente disponibili. Se ci\u00f2 non fosse possibile, la sottoquery finisce in una tabella temporanea \u2013 in tal caso, se fattibile, le assegno una chiave (ad esempio tramite SELECT DISTINCT\/ORDER BY sulle colonne chiave), in modo che i join su di essa non finiscano nel nulla.<\/p>\n\n<pre><code>-- Esempio: EXISTS al posto di IN e tabella derivata compatibile con il merge\nSELECT o.id\nFROM orders o\nWHERE EXISTS (\n  SELECT 1 FROM payments p\n  WHERE p.order_id = o.id AND p.state = 'captured'\n);\n\n-- Derivazione con chiavi univoche\nWITH paid_orders AS (\n  SELECT DISTINCT order_id\n  FROM payments\n  WHERE state = 'captured'\n)\nSELECT o.*\nFROM orders o\nJOIN paid_orders po ON po.order_id = o.id;\n<\/code><\/pre>\n\n<p>Con EXPLAIN FORMAT=JSON verifico se <strong>materializzato<\/strong> oppure <strong>sottoconsultazione dipendente<\/strong> \u00e8 stato eletto e se vi siano condizioni (<strong>condizione pushdown<\/strong>) intervenire con sufficiente anticipo.<\/p>\n\n<h2>Partizionamento e potatura<\/h2>\n\n<p>Il partizionamento non sostituisce gli indici, ma pu\u00f2 <strong>Volume di dati per accesso<\/strong> ridurlo drasticamente. L\u2019Optimizer esegue una potatura corretta solo se il predicato soddisfa il <strong>Chiave di partizione<\/strong> sia unidimensionale e non venga reso irriconoscibile dalle funzioni. Per questo motivo evito espressioni come DATE(created_at) nella clausola WHERE sulle tabelle partizionate e utilizzo invece i limiti di intervallo. EXPLAIN mostra quali partizioni vengono lette; intervalli ampi indicano una scarsa ottimizzazione (pruning).<\/p>\n\n<p>Un numero eccessivo di partizioni di piccole dimensioni aumenta il sovraccarico di pianificazione. Scelgo quindi una granularit\u00e0 adeguata (ad esempio mensile anzich\u00e9 giornaliera), mantengo aggiornate le statistiche per ciascuna partizione (ANALYZE PARTITION) e verifico se gli indici importanti sono presenti localmente nelle partizioni. Nei progetti di migrazione tengo conto dell\u2019impatto sulla replica e sul backup: entrambi questi fattori influenzano il livello di aggressivit\u00e0 con cui eseguo la partizionatura.<\/p>\n\n<h2>Sargability e modelli di riscrittura<\/h2>\n\n<p>La leva pi\u00f9 semplice rimane <strong>Trasportabilit\u00e0 della bara<\/strong> \u2013 Condizioni che rendono utilizzabili gli indici. Evito di applicare funzioni alle colonne nel WHERE, riduco le costanti a livello di colonna e, se necessario, scompongo le condizioni OR in <strong>UNIONE TUTTI<\/strong>. Per le ricerche LIKE senza anchor iniziale (\"%foo\") un indice BTREE non serve a nulla; in questo caso sto valutando di utilizzare la ricerca full-text o un servizio di ricerca adeguato. Per i calcoli utilizzo <strong>colonne generate indicizzate<\/strong>, in modo che l'ottimizzatore possa riconoscere la logica presente nell'indice.<\/p>\n\n<pre><code>-- Anti-pattern: funzione su colonna\nWHERE DATE(created_at) = '2026-08-01'\n-- Meglio: intervallo sul valore grezzo\nWHERE created_at &gt;= '2026-08-01' AND created_at &lt; &#039;2026-08-02&#039;\n\n-- Anti-pattern: l&#039;operatore OR impedisce l&#039;uso dell&#039;indice\nWHERE status = &#039;open&#039; OR customer_id = 42\n-- Migliore: due ricerche con UNION ALL e un indice dedicato per ciascuna\n(SELECT ... WHERE status = &#039;open&#039;)\nUNION ALL\n(SELECT ... WHERE customer_id = 42&#039;);\n<\/code><\/pre>\n\n<p>Per quanto riguarda gli indici compositi, ritengo che il <strong>regola del prefisso a sinistra<\/strong> Rispetta rigorosamente questa regola: ordina le colonne in base alla selettivit\u00e0 e all\u2019ordinamento che sar\u00e0 necessario in seguito. Se ho bisogno di un ORDER BY in ordine decrescente, ne tengo conto nella struttura dell\u2019indice: in questo modo evito il filesort.<\/p>\n\n<h2>Interruttore dell'ottimizzatore e regolazione fine dei costi<\/h2>\n\n<p>Prima di eseguire le query, controllo <strong>optimizer_switch<\/strong> e buffer di memoria. Funzionalit\u00e0 come <em>mrr<\/em>, <em>batched_key_access<\/em>, <em>index_merge<\/em>, <em>semijoin<\/em>, <em>derived_merge<\/em> oppure <em>condizione_pushdown_per_derivato<\/em> possono essere personalizzati per ogni sessione. Attivo i candidati in modo mirato per una sessione di test, effettuo una misurazione con EXPLAIN ANALYZE e ripristino lo stato precedente se l\u2019effetto non si verifica. Il percorso di join beneficia di una quantit\u00e0 sufficiente di <strong>join_buffer_size<\/strong>; grandi variet\u00e0 di <strong>ordinamento_buffer_size<\/strong>. Allo stesso tempo, tengo sotto controllo i buffer in relazione alla concorrenza, in modo che il server non entri in modalit\u00e0 di swapping sotto carico parallelo.<\/p>\n\n<p>A livello di costi, se necessario, adeguo gli elementi gi\u00e0 menzionati <strong>costi_ottimizzatore<\/strong> in microsecondi. La mia linea guida: piccoli passi reversibili con punti di misurazione documentati. Utilizzo <strong>COSTO_ULTIMA_QUERY<\/strong> per verificare la plausibilit\u00e0 ed eseguire misurazioni ripetute con valori dei parametri realistici, poich\u00e9 i piani possono dipendere fortemente da valori letterali concreti.<\/p>\n\n<h2>Stabilit\u00e0 del piano, regressioni e flusso di lavoro del team<\/h2>\n\n<p>Anche un buon piano pu\u00f2 essere compromesso dall'aumento del volume dei dati o da un cambio di versione <strong>ribaltare<\/strong>. Per questo motivo raccolgo informazioni sul piano di esecuzione: hash delle query, JSON di EXPLAIN, estratti della traccia dell\u2019ottimizzatore e tempi di esecuzione di EXPLAIN ANALYZE. Le modifiche agli indici e le riscritture vengono effettuate tramite pull request, corredate da prove \u201cprima e dopo\u201d. Negli ambienti CI\/CD verifico automaticamente le query critiche rispetto a set di dati rappresentativi. In questo modo riesco a <strong>Piani di regressione<\/strong> di buon mattino.<\/p>\n\n<p>Per i casi delicati, ritengo che <strong>Suggerimenti<\/strong> (FORCE INDEX, STRAIGHT_JOIN, optimizer_switch per ogni query) sono disponibili come ultima opzione, ma utilizzale con parsimonia e fissando una data di scadenza. \u00c8 meglio risolvere le cause alla radice: statistiche, indici, formulazione. Nei team, una guida sintetica sulla \u201csargability\u201d, sulla progettazione degli indici e sulla disciplina di misurazione garantisce che le nuove funzionalit\u00e0 non introducano, inosservate, problemi di prestazioni.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/08\/mariadb-query-optimizer-7832.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Breve sintesi: dal progetto alla realizzazione<\/h2>\n\n<p>Chi pu\u00f2 utilizzare il <strong>Piano<\/strong> Chi comprende, controlla le prestazioni. Le fasi di parsing, preparazione, ottimizzazione ed esecuzione spiegano dove si perde tempo. Il modello di costo basato sul tempo a partire dalla versione 11.0, le statistiche aggiornate e gli istogrammi rendono le stime affidabili. EXPLAIN, EXPLAIN ANALYZE e l\u2019Optimizer Trace garantiscono trasparenza, che traduco in misure concrete. Con una strategia di indicizzazione ben strutturata, una progettazione chiara delle query e un\u2019infrastruttura adeguata, le query MariaDB forniscono risposte costantemente rapide.<\/p>","protected":false},"excerpt":{"rendered":"<p>Scopri come funziona internamente l\u2019ottimizzatore di query di MariaDB, come analizzare il piano di esecuzione SQL con EXPLAIN e come implementare tecniche pratiche di ottimizzazione del database, compresi alcuni consigli per applicazioni web ad alte prestazioni.<\/p>","protected":false},"author":1,"featured_media":20923,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[781],"tags":[],"class_list":["post-20930","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-datenbanken-administration-anleitungen"],"acf":[],"_wp_attached_file":null,"_wp_attachment_metadata":null,"litespeed-optimize-size":null,"litespeed-optimize-set":null,"_elementor_source_image_hash":null,"_wp_attachment_image_alt":null,"stockpack_author_name":null,"stockpack_author_url":null,"stockpack_provider":null,"stockpack_image_url":null,"stockpack_license":null,"stockpack_license_url":null,"stockpack_modification":null,"color":null,"original_id":null,"original_url":null,"original_link":null,"unsplash_location":null,"unsplash_sponsor":null,"unsplash_exif":null,"unsplash_attachment_metadata":null,"_elementor_is_screenshot":null,"surfer_file_name":null,"surfer_file_original_url":null,"envato_tk_source_kit":null,"envato_tk_source_index":null,"envato_tk_manifest":null,"envato_tk_folder_name":null,"envato_tk_builder":null,"envato_elements_download_event":null,"_menu_item_type":null,"_menu_item_menu_item_parent":null,"_menu_item_object_id":null,"_menu_item_object":null,"_menu_item_target":null,"_menu_item_classes":null,"_menu_item_xfn":null,"_menu_item_url":null,"_trp_menu_languages":null,"rank_math_primary_category":null,"rank_math_title":null,"inline_featured_image":null,"_yoast_wpseo_primary_category":null,"rank_math_schema_blogposting":null,"rank_math_schema_videoobject":null,"_oembed_049c719bc4a9f89deaead66a7da9fddc":null,"_oembed_time_049c719bc4a9f89deaead66a7da9fddc":null,"_yoast_wpseo_focuskw":null,"_yoast_wpseo_linkdex":null,"_oembed_27e3473bf8bec795fbeb3a9d38489348":null,"_oembed_c3b0f6959478faf92a1f343d8f96b19e":null,"_trp_translated_slug_en_us":null,"_wp_desired_post_slug":null,"_yoast_wpseo_title":null,"tldname":null,"tldpreis":null,"tldrubrik":null,"tldpolicylink":null,"tldsize":null,"tldregistrierungsdauer":null,"tldtransfer":null,"tldwhoisprivacy":null,"tldregistrarchange":null,"tldregistrantchange":null,"tldwhoisupdate":null,"tldnameserverupdate":null,"tlddeletesofort":null,"tlddeleteexpire":null,"tldumlaute":null,"tldrestore":null,"tldsubcategory":null,"tldbildname":null,"tldbildurl":null,"tldclean":null,"tldcategory":null,"tldpolicy":null,"tldbesonderheiten":null,"tld_bedeutung":null,"_oembed_d167040d816d8f94c072940c8009f5f8":null,"_oembed_b0a0fa59ef14f8870da2c63f2027d064":null,"_oembed_4792fa4dfb2a8f09ab950a73b7f313ba":null,"_oembed_33ceb1fe54a8ab775d9410abf699878d":null,"_oembed_fd7014d14d919b45ec004937c0db9335":null,"_oembed_21a029d076783ec3e8042698c351bd7e":null,"_oembed_be5ea8a0c7b18e658f08cc571a909452":null,"_oembed_a9ca7a298b19f9b48ec5914e010294d2":null,"_oembed_f8db6b27d08a2bb1f920e7647808899a":null,"_oembed_168ebde5096e77d8a89326519af9e022":null,"_oembed_cdb76f1b345b42743edfe25481b6f98f":null,"_oembed_87b0613611ae54e86e8864265404b0a1":null,"_oembed_27aa0e5cf3f1bb4bc416a4641a5ac273":null,"_oembed_time_27aa0e5cf3f1bb4bc416a4641a5ac273":null,"_tldname":null,"_tldclean":null,"_tldpreis":null,"_tldcategory":null,"_tldsubcategory":null,"_tldpolicy":null,"_tldpolicylink":null,"_tldsize":null,"_tldregistrierungsdauer":null,"_tldtransfer":null,"_tldwhoisprivacy":null,"_tldregistrarchange":null,"_tldregistrantchange":null,"_tldwhoisupdate":null,"_tldnameserverupdate":null,"_tlddeletesofort":null,"_tlddeleteexpire":null,"_tldumlaute":null,"_tldrestore":null,"_tldbildname":null,"_tldbildurl":null,"_tld_bedeutung":null,"_tldbesonderheiten":null,"_oembed_ad96e4112edb9f8ffa35731d4098bc6b":null,"_oembed_8357e2b8a2575c74ed5978f262a10126":null,"_oembed_3d5fea5103dd0d22ec5d6a33eff7f863":null,"_eael_widget_elements":null,"_oembed_0d8a206f09633e3d62b95a15a4dd0487":null,"_oembed_time_0d8a206f09633e3d62b95a15a4dd0487":null,"_aioseo_description":null,"_eb_attr":null,"_eb_data_table":null,"_oembed_819a879e7da16dd629cfd15a97334c8a":null,"_oembed_time_819a879e7da16dd629cfd15a97334c8a":null,"_acf_changed":null,"_wpcode_auto_insert":null,"_edit_last":null,"_edit_lock":null,"_oembed_e7b913c6c84084ed9702cb4feb012ddd":null,"_oembed_bfde9e10f59a17b85fc8917fa7edf782":null,"_oembed_time_bfde9e10f59a17b85fc8917fa7edf782":null,"_oembed_03514b67990db061d7c4672de26dc514":null,"_oembed_time_03514b67990db061d7c4672de26dc514":null,"rank_math_news_sitemap_robots":null,"rank_math_robots":null,"_eael_post_view_count":"124","_trp_automatically_translated_slug_ru_ru":null,"_trp_automatically_translated_slug_et":null,"_trp_automatically_translated_slug_lv":null,"_trp_automatically_translated_slug_fr_fr":null,"_trp_automatically_translated_slug_en_us":null,"_wp_old_slug":null,"_trp_automatically_translated_slug_da_dk":null,"_trp_automatically_translated_slug_pl_pl":null,"_trp_automatically_translated_slug_es_es":null,"_trp_automatically_translated_slug_hu_hu":null,"_trp_automatically_translated_slug_fi":null,"_trp_automatically_translated_slug_ja":null,"_trp_automatically_translated_slug_lt_lt":null,"_elementor_edit_mode":null,"_elementor_template_type":null,"_elementor_version":null,"_elementor_pro_version":null,"_wp_page_template":null,"_elementor_page_settings":null,"_elementor_data":null,"_elementor_css":null,"_elementor_conditions":null,"_happyaddons_elements_cache":null,"_oembed_75446120c39305f0da0ccd147f6de9cb":null,"_oembed_time_75446120c39305f0da0ccd147f6de9cb":null,"_oembed_3efb2c3e76a18143e7207993a2a6939a":null,"_oembed_time_3efb2c3e76a18143e7207993a2a6939a":null,"_oembed_59808117857ddf57e478a31d79f76e4d":null,"_oembed_time_59808117857ddf57e478a31d79f76e4d":null,"_oembed_965c5b49aa8d22ce37dfb3bde0268600":null,"_oembed_time_965c5b49aa8d22ce37dfb3bde0268600":null,"_oembed_81002f7ee3604f645db4ebcfd1912acf":null,"_oembed_time_81002f7ee3604f645db4ebcfd1912acf":null,"_elementor_screenshot":null,"_oembed_7ea3429961cf98fa85da9747683af827":null,"_oembed_time_7ea3429961cf98fa85da9747683af827":null,"_elementor_controls_usage":null,"_elementor_page_assets":[],"_elementor_screenshot_failed":null,"theplus_transient_widgets":null,"_eael_custom_js":null,"_wp_old_date":null,"_trp_automatically_translated_slug_it_it":null,"_trp_automatically_translated_slug_pt_pt":null,"_trp_automatically_translated_slug_zh_cn":null,"_trp_automatically_translated_slug_nl_nl":null,"_trp_automatically_translated_slug_pt_br":null,"_trp_automatically_translated_slug_sv_se":null,"rank_math_analytic_object_id":null,"rank_math_internal_links_processed":"1","_trp_automatically_translated_slug_ro_ro":null,"_trp_automatically_translated_slug_sk_sk":null,"_trp_automatically_translated_slug_bg_bg":null,"_trp_automatically_translated_slug_sl_si":null,"litespeed_vpi_list":null,"litespeed_vpi_list_mobile":null,"rank_math_seo_score":null,"rank_math_contentai_score":null,"ilj_limitincominglinks":null,"ilj_maxincominglinks":null,"ilj_limitoutgoinglinks":null,"ilj_maxoutgoinglinks":null,"ilj_limitlinksperparagraph":null,"ilj_linksperparagraph":null,"ilj_blacklistdefinition":null,"ilj_linkdefinition":null,"_eb_reusable_block_ids":null,"rank_math_focus_keyword":"MariaDB Optimizer","rank_math_og_content_image":null,"_yoast_wpseo_metadesc":null,"_yoast_wpseo_content_score":null,"_yoast_wpseo_focuskeywords":null,"_yoast_wpseo_keywordsynonyms":null,"_yoast_wpseo_estimated-reading-time-minutes":null,"rank_math_description":null,"surfer_last_post_update":null,"surfer_last_post_update_direction":null,"surfer_keywords":null,"surfer_location":null,"surfer_draft_id":null,"surfer_permalink_hash":null,"surfer_scrape_ready":null,"_thumbnail_id":"20923","footnotes":null,"_links":{"self":[{"href":"https:\/\/webhosting.de\/it\/wp-json\/wp\/v2\/posts\/20930","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/webhosting.de\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/webhosting.de\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/webhosting.de\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/webhosting.de\/it\/wp-json\/wp\/v2\/comments?post=20930"}],"version-history":[{"count":0,"href":"https:\/\/webhosting.de\/it\/wp-json\/wp\/v2\/posts\/20930\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/webhosting.de\/it\/wp-json\/wp\/v2\/media\/20923"}],"wp:attachment":[{"href":"https:\/\/webhosting.de\/it\/wp-json\/wp\/v2\/media?parent=20930"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/webhosting.de\/it\/wp-json\/wp\/v2\/categories?post=20930"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/webhosting.de\/it\/wp-json\/wp\/v2\/tags?post=20930"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}