lock and nolock in sql

lock and nolock in sql

Ho visto questa scena ripetersi troppe volte per contarle: un lunedì mattina, il database di produzione è piantato, il sito e-commerce non carica i carrelli e il CTO urla nel corridoio perché stiamo perdendo cinquemila euro ogni dieci minuti di inattività. Il colpevole? Uno sviluppatore junior, o peggio, un senior pigro, ha inserito un suggerimento di tabella senza capire come funziona Lock And NoLock In SQL durante un aggiornamento massivo di listini. Pensava di velocizzare la lettura, invece ha creato un blocco a catena che ha saturato le connessioni disponibili in meno di sessanta secondi. Non è un errore di sintassi, è un errore di strategia che nasce dalla paura dei blocchi, e la paura nel mondo dei database porta sempre a decisioni disastrose.

Il mito della bacchetta magica chiamata Lock And NoLock In SQL

Molti programmatori trattano l'istruzione di lettura sporca come se fosse un tasto "vai più veloce" da spalmare su ogni query SELECT. L'idea è seducente: non voglio che le mie letture vengano bloccate dalle scritture, quindi dico al motore del database di ignorare i segnali di stop. Ho visto aziende intere adottare questa politica come standard di sviluppo, convinte di aver risolto i problemi di concorrenza. La realtà è che stavano solo nascondendo la polvere sotto il tappeto. Quando usi questo approccio, stai accettando di leggere dati che potrebbero non esistere mai. Se una transazione sta aggiornando il saldo di un conto e tu leggi quel valore mentre l’operazione è a metà, potresti vedere una cifra che scompare un millisecondo dopo a causa di un rollback.

In un sistema di contabilità per una nota azienda di logistica con cui ho lavorato, questo vizio aveva portato a discrepanze inventariali per oltre ottantamila euro. Il sistema leggeva dati "fantasma" per generare i report di spedizione, spedendo merce che tecnicamente non era ancora stata confermata a magazzino. Non puoi ottimizzare le prestazioni sacrificando l'integrità dei dati se non sei disposto a pagare il conto quando i conti non tornano. La soluzione non è evitare i blocchi a ogni costo, ma capire perché accadono. Il blocco è un meccanismo di protezione, non un nemico da abbattere. Invece di forzare la lettura, dovresti analizzare gli indici. Spesso, una query lenta che causa blocchi non ha bisogno di un permesso di lettura sporca, ha bisogno di un indice coprente che riduca il tempo di occupazione delle risorse.

L'illusione che NoLock impedisca i deadlocks

C'è questa convinzione pericolosa secondo cui usare il suggerimento di saltare i blocchi renda il codice immune ai deadlock. Non c'è niente di più falso. Ho passato notti intere a debuggare grafici di deadlock in SQL Server dove apparivano query con istruzioni di lettura non bloccante. Il motivo è tecnico e spesso ignorato: anche se dici al database di non rispettare i blocchi di condivisione, il motore deve comunque acquisire dei blocchi di stabilità dello schema (Sch-S) per assicurarsi che nessuno cambi la struttura della tabella mentre la stai leggendo.

Se hai un processo pesante che sta ricostruendo un indice o modificando una colonna, la tua query "veloce" rimarrà comunque in coda. Peggio ancora, ho visto situazioni in cui l'uso massiccio di queste tecniche ha causato errori di allocazione delle pagine. Mentre la tua query legge senza protezione, una riga potrebbe spostarsi di pagina a causa di un aggiornamento contemporaneo. Il risultato? La tua query legge la stessa riga due volte o la salta completamente. In un report finanziario trimestrale, saltare una riga da un milione di euro non è un piccolo inconveniente tecnico, è un disastro che richiede giorni di riconciliazione manuale. Invece di affidarti ciecamente a queste scorciatoie, dovresti studiare i livelli di isolamento come lo Snapshot Isolation. Microsoft stessa, nella documentazione ufficiale, suggerisce che l'isolamento basato sul versioning delle righe è quasi sempre una scelta migliore rispetto alla lettura sporca, perché garantisce coerenza senza bloccare i lettori.

Strategie errate nella gestione delle transazioni lunghe

Un altro errore classico che prosciuga il budget è l'uso di transazioni chilometriche. Ho visto script di migrazione dati che aprivano una transazione, aggiornavano tre milioni di righe e poi facevano altre operazioni di logica applicativa prima di chiudere. In quel lasso di tempo, l'intera tabella era praticamente sotto sequestro. Qui è dove qualcuno di solito interviene aggiungendo un'istruzione Lock And NoLock In SQL nelle altre procedure per "sbloccare la situazione". È come mettere un cerotto su un'arteria recisa.

🔗 Leggi di più: la casta dei meta baroni

La soluzione corretta è il batching. Invece di un unico blocco enorme, devi spezzettare l'operazione. Se devi aggiornare un milione di record, fallo in blocchi da cinquemila. Questo permette al database di respirare, ai log delle transazioni di non esplodere e, soprattutto, dà modo agli altri processi di infilarsi tra un batch e l'altro. Ho ridotto i tempi di blocco di un database governativo da ore a pochi secondi semplicemente eliminando le transazioni giganti e implementando cicli di aggiornamento controllati con piccole pause tra uno e l'altro. È un lavoro più faticoso a livello di codice, ma è l'unico modo professionale per gestire la concorrenza in sistemi ad alto traffico.

Prima e dopo: un caso reale di ottimizzazione delle contese

Per capire l'impatto di un cambio di mentalità, guardiamo cosa è successo in una piattaforma di prenotazioni alberghiere che gestivo tre anni fa.

Prima della correzione: Il sistema soffriva di rallentamenti costanti durante le ore di punta. Gli sviluppatori avevano riempito il codice di suggerimenti NoLock su ogni query di ricerca disponibilità. Pensavano di essere furbi. Tuttavia, durante le procedure di aggiornamento delle tariffe dei partner, che avvenivano via API ogni pochi minuti, le query di ricerca restituivano prezzi sbagliati o, peggio, camere già vendute. Il tasso di overbooking era salito al 4%. Ogni volta che un cliente si presentava in hotel e non trovava la camera, l'azienda doveva rimborsare il soggiorno e pagare una penale. Il costo medio di questi errori era di circa milletrecento euro al giorno, senza contare il danno d'immagine.

Dopo la correzione: Abbiamo rimosso tutti i suggerimenti di lettura sporca. Inizialmente, come previsto, i blocchi sono aumentati e il sistema sembrava più lento. Ma invece di tornare indietro, abbiamo fatto tre cose: attivato il Read Committed Snapshot Isolation (RCSI) a livello di database, creato indici filtrati per le query di ricerca e spostato i processi di aggiornamento tariffe su una logica di piccoli batch. Dopo due settimane di monitoraggio, l'overbooking tecnico è sceso allo 0%. Le ricerche erano fluide non perché ignoravano i blocchi, ma perché non ne generavano più. Il carico sulla CPU è sceso del 22% perché il motore del database non doveva più gestire migliaia di tentativi di lettura su dati in movimento. Abbiamo salvato l'azienda da perdite costanti migliorando la qualità del dato, non aggirando le regole del motore SQL.

Il pericolo degli indici mancanti mascherato dai lock

Ho notato un pattern ricorrente: quando qualcuno si lamenta che "il database è lento a causa dei blocchi", nove volte su dieci il problema non sono i blocchi, ma gli indici. Se una SELECT deve fare un table scan su una tabella da dieci milioni di righe per trovare un singolo ID, terrà impegnata una risorsa per molto tempo. Se nel frattempo arriva una UPDATE, quest'ultima dovrà aspettare. Qui l'utente inesperto pensa che il problema sia la concorrenza e cerca di risolverlo modificando il comportamento dei blocchi.

Se invece aggiungi l'indice corretto, la SELECT dura tre millisecondi invece di quattro secondi. Il blocco esiste ancora, ma è così breve che nessuno se ne accorge. Ho visto database server con dischi SSD ultra-veloci e 512 GB di RAM messi in ginocchio da query scritte male che cercavano di bypassare i blocchi. Non puoi risolvere con il software un problema di design strutturale. Prima di toccare qualsiasi impostazione relativa ai blocchi, devi generare un piano di esecuzione e vedere se stai facendo scansioni inutili. Se vedi un operatore di "Table Scan" o "Index Scan" su una tabella grande, quella è la tua perdita di denaro, non il meccanismo di locking.

La gestione sbagliata degli escalation di blocco

In SQL Server, se una query tocca più di cinquemila righe, il sistema spesso decide di smettere di gestire i singoli blocchi di riga e blocca l'intera tabella. È una scelta di efficienza per risparmiare memoria. Molti amministratori di sistema, presi dal panico, cercano di disabilitare questo comportamento o forzano letture non protette per evitare di rimanere bloccati fuori dalla propria tabella.

Nella mia esperienza con un fornitore di servizi SaaS, questa "soluzione" aveva causato il congelamento totale del sistema durante i backup notturni. La soluzione non era impedire l'escalation, ma capire perché la query toccava così tante righe in una volta sola. Abbiamo ridisegnato il processo per operare su intervalli di chiavi primarie più piccoli. Invece di fare una DELETE massiva, abbiamo usato un approccio a soglie. Questo ha permesso al gestore dei blocchi di rimanere a livello di riga o di pagina, lasciando il resto della tabella disponibile per gli utenti attivi. È la differenza tra chiudere un'intera autostrada per lavori e chiudere solo una corsia permettendo al traffico di defluire.

👉 Vedi anche: motore fiat scudo 1.9

Controllo della realtà

Essere un esperto di database non significa conoscere tutti i comandi a memoria, significa capire che ogni scelta ha un prezzo. Se pensi di poter usare scorciatoie per gestire la concorrenza senza studiare profondamente come il tuo specifico motore di database gestisce la memoria e i file fisici, stai solo aspettando il momento in cui il sistema crollerà sotto il proprio peso.

Non esiste un pasto gratis. Se vuoi velocità, devi investire in un design degli indici impeccabile e in una scrittura delle query che minimizzi il tempo di esecuzione. Se vuoi coerenza dei dati, devi accettare che i blocchi esistano e imparare a conviverci riducendo la loro durata, non eliminandoli. Ho visto carriere rovinate per un NoLock messo nel posto sbagliato durante un aggiornamento di un database bancario. La realtà è che la maggior parte dei problemi di performance che le persone cercano di risolvere con queste tecniche sono in realtà sintomi di un'architettura pigra. Smetti di cercare il trucco magico e inizia a guardare i piani di esecuzione e le statistiche di attesa. Solo lì troverai la verità su cosa sta rallentando il tuo business.

  • Analizza sempre le statistiche di attesa (wait stats) prima di agire.
  • Testa ogni modifica sotto un carico di stress che sia almeno il doppio del tuo traffico normale.
  • Non implementare mai soluzioni globali per problemi che sono localizzati in singole query.
  • Monitora costantemente la frammentazione degli indici, poiché influisce direttamente sulla durata dei blocchi.
  • Ricorda che la coerenza dei dati vale sempre più di un secondo di latenza risparmiato male.

Padroneggiare questi concetti richiede anni di errori, ma puoi iniziare oggi smettendo di applicare soluzioni facili a problemi complessi. Non è il database che è lento, è il modo in cui gli chiedi i dati che lo rende tale.

GB

Giuseppe Barbieri

Giuseppe Barbieri ha collaborato con diverse redazioni online, costruendo un percorso centrato su affidabilità e qualità informativa.