▦ Dati e database

Indici nei database: come funzionano e quando crearli

Cos'è un indice B-tree, perché velocizza le ricerche, quanto costa in scrittura e come scegliere le colonne giuste, anche negli indici composti.

Quando una query che prima rispondeva in un istante inizia a impiegare secondi, la causa più frequente è un indice mancante. Allo stesso tempo, chi scopre gli indici tende a crearne ovunque, rallentando le scritture e sprecando spazio. Capire come funzionano davvero permette di metterli dove servono e solo lì.

Il problema che risolvono

Senza indici, per trovare i clienti con un certo cognome il database deve leggere ogni riga della tabella e controllarla. Si chiama scansione completa, o full table scan. Su mille righe non si nota, su dieci milioni sì.

Un indice è una struttura separata che contiene i valori di una o più colonne, ordinati, ciascuno accompagnato dal riferimento alla riga corrispondente. L'analogia classica è l'indice analitico di un libro: invece di sfogliare tutte le pagine per trovare un argomento, si cerca la voce in ordine alfabetico e si va direttamente alla pagina indicata.

Come è fatto un indice B-tree

La struttura più diffusa nei database relazionali è il B-tree, o una sua variante. È un albero bilanciato in cui:

  • la radice e i nodi intermedi contengono valori di separazione che indicano in quale ramo proseguire;
  • le foglie contengono i valori ordinati e i puntatori alle righe;
  • tutte le foglie si trovano alla stessa profondità.

Poiché ogni nodo contiene molti valori, l'albero è molto largo e poco profondo. Anche con milioni di righe bastano pochi livelli, quindi pochi accessi, per arrivare al valore cercato. In più, essendo le foglie ordinate e collegate tra loro, l'indice è efficiente anche per gli intervalli: «tutti gli ordini tra il 1° e il 15 marzo» si trova scendendo fino al primo valore e scorrendo le foglie fino all'ultimo.

Che cosa un indice accelera

Un indice B-tree su una colonna è utile per:

  • uguaglianze: WHERE email = 'anna@esempio.it';
  • intervalli: WHERE data_ordine BETWEEN '2025-03-01' AND '2025-03-15';
  • ordinamenti: ORDER BY data_ordine, perché i valori sono già in ordine;
  • JOIN: la colonna di chiave esterna usata per unire le tabelle;
  • ricerche per prefisso: WHERE cognome LIKE 'Ros%'.

Non aiuta, invece, nelle ricerche con il carattere jolly iniziale, come LIKE '%ossi', perché l'ordine alfabetico parte dall'inizio della stringa. Per questi casi esistono strutture diverse, come gli indici per la ricerca testuale.

Gli indici composti e l'ordine delle colonne

Un indice può contenere più colonne. In questo caso l'ordine conta moltissimo:

CREATE INDEX idx_ordini_cliente_data
  ON ordini (id_cliente, data_ordine);

Questo indice è ordinato prima per cliente e, a parità di cliente, per data. Come un elenco telefonico ordinato per cognome e poi per nome, funziona per:

  • WHERE id_cliente = 42
  • WHERE id_cliente = 42 AND data_ordine >= '2025-01-01'
  • WHERE id_cliente = 42 ORDER BY data_ordine

Ma non è di grande aiuto per WHERE data_ordine >= '2025-01-01' da sola, così come un elenco ordinato per cognome non aiuta a trovare tutte le persone di nome Maria. La regola pratica è mettere per prime le colonne usate con l'uguaglianza e dopo quelle usate per intervalli o ordinamenti.

Il costo degli indici

Gli indici non sono gratuiti:

  1. Scritture più lente. Ogni INSERT, UPDATE o DELETE deve aggiornare anche tutti gli indici della tabella.
  2. Spazio su disco e in memoria. Un indice può occupare una frazione importante della tabella stessa, e funziona meglio quando sta in memoria.
  3. Manutenzione. Indici inutilizzati vanno comunque aggiornati e salvati nei backup.

Per questo una tabella che riceve molte scritture e poche letture va indicizzata con prudenza, mentre una tabella consultata di continuo e aggiornata di rado può permettersi più indici.

Selettività: quando un indice non serve

Un indice è efficace quando filtra poche righe rispetto al totale. Su una colonna «attivo» con due soli valori, dove il 95% delle righe vale vero, cercare i record attivi con l'indice è più lento che leggere la tabella. L'ottimizzatore del database lo sa e spesso ignora l'indice. Alcuni database permettono invece indici parziali, che contengono solo le righe di interesse, per esempio solo quelle con attivo = false.

Come decidere quali indici creare

Un metodo pratico:

  1. partire dalle query reali, non da ipotesi: i log delle query lente sono la fonte migliore;
  2. verificare con il piano di esecuzione se la query fa una scansione completa;
  3. creare l'indice più mirato possibile e controllare di nuovo il piano;
  4. periodicamente cercare gli indici mai usati, che molti database segnalano nelle loro statistiche, e valutarne la rimozione.

Le chiavi primarie e i vincoli UNIQUE creano già un indice automaticamente. Le chiavi esterne, in molti database, no: indicizzarle è spesso il primo intervento utile.

In sintesi

  • Un indice è una copia ordinata di alcune colonne che evita di leggere tutta la tabella.
  • Il B-tree è poco profondo e gestisce bene uguaglianze, intervalli e ordinamenti.
  • Negli indici composti l'ordine delle colonne determina quali query ne beneficiano.
  • Ogni indice rallenta le scritture e occupa spazio: va giustificato da query reali.
  • Le chiavi esterne sono spesso le prime candidate a essere indicizzate.
MM
Marco Marzaduri

Marco Marzaduri si occupa di cybersecurity, informatica forense e sviluppo web. Laureato in informatica e in psicologia, unisce l'analisi tecnica alla comprensione del fattore umano.

Leggi anche