Vediamo un po' cosa fa il livello di isolamento READ COMMITTED. Il suo nome già suggerisce che tutto quello che leggiamo dentro una transazione è già "committato" da altre transazioni. Un po' come nella vita reale, quando crediamo solo ai gossip che sono stati ufficialmente messi a verbale.
Parlando seriamente, questo livello garantisce che una transazione non può vedere cambiamenti fatti da altre transazioni che non sono ancora state confermate. Questo risolve il problema noto come "lettura sporca" (Dirty Read). Però, occhio: i dati che leggi possono cambiare se un'altra transazione fa un commit tra una tua query e l'altra. Questo crea il rischio di "lettura non ripetibile" (Non-Repeatable Read).
In PostgreSQL il livello di isolamento READ COMMITTED è quello di default. È come la modalità predefinita — non devi configurare nulla di speciale per usarlo.
Esempio di utilizzo del livello di isolamento READ COMMITTED
Vediamo come funziona nella pratica. Immagina di avere una tabella accounts che contiene info sugli utenti e i loro saldi:
CREATE TABLE accounts (
account_id SERIAL PRIMARY KEY,
account_name TEXT NOT NULL,
balance NUMERIC(10, 2) NOT NULL
);
INSERT INTO accounts (account_name, balance)
VALUES ('Alice', 1000.00), ('Bob', 500.00);
Ora immaginiamo questa situazione. Sessione 1 e Sessione 2 — i due protagonisti della nostra storia, entrambi lavorano sulla tabella accounts. Ecco cosa succede:
Sessione 1:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_name = 'Alice';
-- Nessun COMMIT o ROLLBACK per ora.
In questo momento dal balance di Alice sono stati temporaneamente tolti 100, ma il risultato non è ancora stato confermato.
Sessione 2:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT balance FROM accounts WHERE account_name = 'Alice';
Risultato: la Sessione 2 vede il saldo di Alice come 1000.00, perché la transazione della Sessione 1 non è ancora finita (COMMIT non eseguito). Il livello READ COMMITTED ci protegge dalla "lettura sporca".
La Sessione 1 chiude la transazione:
COMMIT;
Ora il saldo di Alice è aggiornato e il valore 900.00 è stato fissato nel database.
La Sessione 2 ripete la query:
SELECT balance FROM accounts WHERE account_name = 'Alice';
Risultato: ora la Sessione 2 vede il saldo aggiornato di Alice — 900.00. Nota che il risultato è diverso dalla query precedente, che restituiva 1000.00. Questo è il problema della "lettura non ripetibile".
Quando usare READ COMMITTED?
Il livello di isolamento READ COMMITTED è un compromesso tra performance e consistenza. Ma ci sono alcuni scenari dove è proprio perfetto:
Operazioni CRUD semplici: quando leggi o aggiorni dati senza relazioni complicate.
Aggiornamento di record: tipo aggiornamenti di massa in una tabella, dove è importante vedere subito i cambiamenti confermati.
Gestione delle transazioni: sistemi di pagamento dove gli utenti devono vedere solo dati confermati.
Però, se fai query analitiche complesse o lavori con grandi quantità di dati, forse ti conviene considerare un altro livello di isolamento, tipo REPEATABLE READ.
Vantaggi e svantaggi del livello di isolamento READ COMMITTED
Il livello di isolamento READ COMMITTED è un po' come la via di mezzo. Ti protegge dai dati sporchi: non vedrai cambiamenti che un'altra transazione ha iniziato ma non ha ancora finito. Nessuno leggerà info "crude" che potrebbero essere subito annullate.
Questa modalità è più veloce dei livelli più restrittivi (REPEATABLE READ o SERIALIZABLE), perché non richiede lock complicati o controlli extra. È abbastanza leggera e allo stesso tempo affidabile — per questo è quella di default e va benissimo per la maggior parte delle attività quotidiane.
Anche se previene la lettura sporca, READ COMMITTED non protegge da:
- "Lettura non ripetibile" (
Non-Repeatable Read): il valore dei dati può cambiare se un'altra transazione fa modifiche tra le tue query. - "Lettura fantasma" (
Phantom Read): un'altra transazione può aggiungere righe che influenzano il risultato della tua query.
Consigli quando lavori con READ COMMITTED
Chiudi sempre le transazioni: non dimenticare di usare COMMIT o ROLLBACK, altrimenti rischi problemi di lock.
Assicurati che il livello di isolamento sia sufficiente: se ti serve la garanzia che i dati non cambino durante la transazione, considera REPEATABLE READ.
Usa gli indici: questo aiuta PostgreSQL a trovare i dati più in fretta e applicare le modifiche.
Esempio: gestione degli ordini
Supponiamo di avere una tabella orders dove sono salvati i dati degli ordini:
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending'
);
INSERT INTO orders (customer_name, status)
VALUES ('Alice', 'pending'), ('Bob', 'pending');
Vogliamo aggiornare lo status degli ordini che sono in stato "pending":
BEGIN;
SELECT * FROM orders WHERE status = 'pending';
UPDATE orders SET status = 'completed' WHERE status = 'pending';
COMMIT;
Se durante l'esecuzione un'altra transazione aggiunge un nuovo ordine con status "pending" e fa COMMIT, la nostra transazione non considererà quella riga, perché è stata aggiunta dopo l'inizio della lettura.
Questo è un esempio di "lettura fantasma". Se vuoi evitare queste situazioni, devi usare SERIALIZABLE.
Il livello di isolamento READ COMMITTED è la scelta di default per la maggior parte dei database, incluso PostgreSQL. Ti protegge dalle letture sporche, il che lo rende una buona opzione per la maggior parte delle operazioni standard. Però, in scenari che richiedono una consistenza più rigorosa, potrebbero servire livelli di isolamento più restrittivi. La scelta del livello di isolamento dipende dai tuoi obiettivi specifici e dai requisiti di performance.
GO TO FULL VERSION