In SQL Server è possibile utilizzare specifiche clausole a livello di tabella (table hint) per modificare il comportamento predefinito dell’ottimizzatore durante l’esecuzione di una query. Una delle clausole più utilizzate, e spesso abusate, è WITH(NOLOCK).
Molto frequentemente presso istanze dei nostri clienti ho riscontrato la presenza di numerose query di tipo SELECT accompagnate da NOLOCK, spesso implementate senza una reale comprensione delle implicazioni che questo può comportare. Questo documento analizza il funzionamento di tale clausola a livello di engine SQL Server, i suoi vantaggi in termini di performance, le sue criticità dal punto di vista dell’integrità dei dati, e fornisce indicazioni su un utilizzo più consapevole basato su casi d’uso specifici.
Sintassi e funzionamento interno
Sintassi della clausola NOLOCK
La clausola NOLOCK può essere utilizzata esclusivamente nelle query di tipo SELECT, con la seguente sintassi:
Non è possibile utilizzarla in operazioni di UPDATE o DELETE, poiché queste operazioni richiedono necessariamente l’acquisizione di lock esclusivi.
Meccanismo di funzionamento interno
Utilizzando NOLOCK, SQL Server opera nei seguenti modi:
- Ignora i lock di tipo S (Shared) e X (Exclusive) presenti sulle righe, pagine e tabelle.
- Non acquisisce lock di tipo S durante la lettura delle righe.
Tecnicamente, questa clausola sovrascrive il livello di isolamento predefinito (READ COMMITTED) dell’istanza SQL Server, simulando il livello READ UNCOMMITTED specificamente per la singola query. Questo equivale a impostare temporaneamente il livello di isolamento della sessione utilizzando:
Impatto sul sistema di lock di SQL Server
Sistema di lock in SQL Server
Per comprendere a fondo le implicazioni di NOLOCK, è necessario esaminare il sistema di lock di SQL Server:
Gerarchia dei lock e granularità
SQL Server implementa una gerarchia di lock che va dal livello più ampio (DATABASE) al più granulare (ROW):
DATABASE → TABLE → PAGE → ROW
Con NOLOCK, la query ignora i lock a tutti i livelli eccetto quello di schema (SCH-S), il che permette di leggere tabelle anche mentre vengono modificate da altre sessioni.
Controindicazioni nell’uso di NOLOCK
L’uso di NOLOCK potrebbe sembrare quindi sempre conveniente e performante. Tuttavia esistono degli effetti collaterali che vanno ben valutati per poterne fare un uso consapevole.
1. Dirty Read (Lettura Sporca)
Una dirty read si verifica quando una query con NOLOCK legge dati non ancora confermati (COMMIT). Se la transazione originaria viene annullata (ROLLBACK), la query avrà letto dati non validi e potenzialmente inconsistenti.
Scenario tecnico
Se la Sessione 2 ha già letto il valore 500, significa che ha ottenuto un dato mai confermato e potenzialmente errato. In un’applicazione reale, questo può portare a:
- Calcoli errati (es. totali di ordini)
- Decisioni operative basate su dati non definitivi
- Report con dati inconsistenti
2. Non-Repeatable Read (Lettura Non Ripetibile)
Una non-repeatable read si verifica quando una query legge più volte lo stesso dato all’interno di una transazione e ottiene risultati diversi a causa di una modifica effettuata da un’altra transazione.
Scenario tecnico
Questo scenario è particolarmente problematico quando:
- Si eseguono calcoli in più passaggi basati su dati correlati.
- Si prendono decisioni dentro transazioni in base a letture multiple dello stesso dato.
3. Phantom Read (Lettura Fantasma)
Una phantom read si verifica quando una query eseguita più volte all’interno della stessa transazione restituisce un insieme diverso di righe a causa di un’inserzione o cancellazione di dati effettuata da un’altra transazione.
Scenario tecnico
4. Lost Updates (Aggiornamenti Perduti)
Un problema grave, seppur indiretto, legato all’uso di NOLOCK è il rischio di lost updates quando si legge un dato con NOLOCK e poi lo si aggiorna basandosi su tale lettura.
Scenario tecnico
5. Problemi di scan inconsistenti
Un aspetto meno noto, ma altrettanto critico, riguarda la possibilità che SQL Server esegua letture inconsistenti delle strutture fisiche delle tabelle (pagine di dati, indici) durante l’esecuzione di una query con NOLOCK.
Problemi specifici
- Missing Rows (Righe Mancanti): Una query con NOLOCK potrebbe saltare righe durante la scansione se le pagine vengono riorganizzate da operazioni concorrenti.
- Duplicate Rows (Righe Duplicate): La stessa riga potrebbe essere letta più volte se le pagine vengono divise durante la lettura.
- 511 Errors: SQL Server può generare errori 511 quando incontra inconsistenze strutturali durante scansioni NOLOCK.
Query con NOLOCK e interazioni con i lock di schema
Sebbene l’utilizzo di NOLOCK permetta di evitare lock esistenti su righe e pagine, è importante notare che SQL Server acquisisce comunque un lock di tipo SCH-S (Schema Stability) sulla tabella interrogata. Questo lock è necessario per garantire la stabilità dello schema e prevenire modifiche alla struttura della tabella durante l’esecuzione della query.
Matrice di compatibilità dei lock di schema
Scenari di blocco con operazioni DDL
Il problema si presenta quando altre transazioni cercano di acquisire un lock di tipo SCH-M (Schema Modification), necessario per operazioni come:
- ALTER TABLE
- ALTER INDEX
- CREATE INDEX
- DROP INDEX
- DROP TABLE
- TRUNCATE TABLE
In questi casi:
- Se una query con NOLOCK è in esecuzione e ha acquisito un lock SCH-S, impedirà a un’operazione DDL di acquisire il lock SCH-M necessario.
- Se un’operazione DDL è in attesa del lock SCH-M, tutte le nuove query con NOLOCK che necessitano di acquisire un lock SCH-S rimarranno in attesa.
Questo scenario è particolarmente problematico durante le operazioni di manutenzione notturna, quando spesso si eseguono:
- Job di reportistica con NOLOCK.
- Job di manutenzione indici e statistiche che necessitano di SCH-M.
Alternative tecniche a NOLOCK
Esistono soluzioni più sicure e spesso altrettanto performanti rispetto all’uso indiscriminato di NOLOCK:
1. Read Committed Snapshot Isolation (RCSI)
RCSI offre la maggior parte dei vantaggi prestazionali di NOLOCK senza le problematiche di integrità dei dati:
RCSI funziona mantenendo versioni dei dati nel TempDB, permettendo alle query di leggere una snapshot consistente senza bloccare o essere bloccate da operazioni di scrittura.
Vantaggi di RCSI rispetto a NOLOCK:
- Elimina dirty read
- Elimina non-repeatable read
- Mantiene la consistenza transazionale
- Offre prestazioni simili in molti scenari
Limitazioni di RCSI:
- Richiede più spazio su TempDB
- Può aumentare la contesa su TempDB
- Non risolve i phantom read
2. Snapshot Isolation Level
Un livello di isolamento ancora più elevato:
Vantaggi di Snapshot Isolation:
- Elimina dirty read, non-repeatable read e phantom read
- Offre consistenza transazionale completa
- Prestazioni migliori di READ COMMITTED in scenari con alta concorrenza
Limitazioni:
- Maggiore utilizzo di TempDB
- Possibili errori di update conflict
3. Ottimizzazione degli indici e delle transazioni
Una strategia complementare consiste nel migliorare le prestazioni attraverso un’adeguata indicizzazione, come per esempio covering indexes che rendono più veloci le query e generano meno lock sugli oggetti coinvolti.
Inoltre, è importante cercare di mantenere aperte delle transazioni per il tempo strettamente necessario. Durante una transazione tutti i lock che vengono generati sugli oggetti coinvolti vengono mantenuti fintanto che la transazione non viene chiusa. E’ fondamentale quindi inserire in una singola transazione le sole operazioni di cui è strettamente necessario garantire l’esecuzione “tutto o niente”, in modo tale da liberare le risorse occupate il prima possibile.
Casi d’uso appropriati per NOLOCK
Nonostante i rischi, esistono scenari in cui l’uso di NOLOCK può essere appropriato:
- Query di sola reportistica dove:
- L’inconsistenza temporanea dei dati è accettabile.
- I report indicano chiaramente che i dati sono “point-in-time” e potenzialmente incompleti.
- Si applicano aggregazioni statistiche che sono poco sensibili a piccole variazioni.
2. Query di monitoraggio o diagnostica dove:
- Lo scopo è ottenere una panoramica approssimativa del sistema.
- Il tempo di risposta è più importante della precisione assoluta.
- I dati non vengono utilizzati per decisioni critiche.
3. Query su tabelle di riferimento quasi statiche dove:
- Le modifiche sono rare e programmate.
- Le query vengono eseguite in momenti in cui non avvengono modifiche.
- L’impatto di eventuali letture sporche è minimo.
Conclusioni e best practices
L’uso della clausola NOLOCK in SQL Server può risultare utile in specifici scenari per migliorare le prestazioni, ma deve essere impiegata con consapevolezza e parsimonia. I rischi di inconsistenza dei dati sono elevati, e nella maggior parte dei casi esistono alternative migliori che garantiscono un compromesso ottimale tra prestazioni e integrità dei dati.
Linee guida per l’uso responsabile:
- Valutare alternative: Prima di utilizzare NOLOCK, considerare RCSI, ottimizzazione degli indici, e altre soluzioni meno problematiche.
- Documentare l’uso: Quando si utilizza NOLOCK, commentare il codice indicando il motivo della scelta e i potenziali rischi.
- Test approfonditi: Verificare che l’applicazione gestisca correttamente le possibili inconsistenze dei dati.
- Monitoraggio continuo: Implementare sistemi di rilevamento per identificare problemi di inconsistenza dei dati.
- Revisione periodica: Rivalutare regolarmente le query che utilizzano NOLOCK per verificare se siano ancora necessarie o se esistano alternative migliori.
- Formare i team: Assicurarsi che gli sviluppatori e i DBA comprendano le implicazioni dell’uso di NOLOCK.
Prima di applicare NOLOCK indiscriminatamente, è essenziale valutare attentamente se un miglioramento dell’indicizzazione o un diverso livello di isolamento possano rappresentare una soluzione più efficace e sicura che garantisca sia prestazioni ottimali che integrità dei dati.
di Matteo Dal Bianco, pubblicato il 26 marzo 2026