NOLOCK : une réponse rapide peut être fausse
Reproduisez une lecture sale avec deux sessions, comprenez les blocages persistants et remplacez NOLOCK sans sacrifier la cohérence.
Un rapport terminé en deux secondes peut être moins utile qu'un rapport qui attend cinq secondes si son total n'a jamais existé dans les données validées. NOLOCK modifie ce qu'une lecture est autorisée à observer. Pour la facturation, les stocks ou les décisions opérationnelles, il s'agit donc d'un changement fonctionnel et pas simplement d'une optimisation.
Reproduire une lecture incorrecte
Utilisez une base jetable et deux fenêtres de requête connectées à cette même base. Exécutez la préparation une fois. Dans la fenêtre A, ouvrez la transaction et laissez-la active. Dans la fenêtre B, lancez la lecture NOLOCK avant de revenir dans A pour annuler la transaction.
CREATE TABLE dbo.NolockDemo (Id int PRIMARY KEY, Balance int NOT NULL);
INSERT dbo.NolockDemo VALUES (1, 100);
BEGIN TRAN;
UPDATE dbo.NolockDemo SET Balance = 900 WHERE Id = 1;
-- Run the reader in window B, then execute:
-- ROLLBACK;
SELECT Balance FROM dbo.NolockDemo WITH (NOLOCK) WHERE Id = 1;
B peut afficher 900 alors que ce montant ne sera jamais validé. Après l'annulation, le solde confirmé reste 100. Une lecture READ COMMITTED utilisant les verrous peut attendre pendant cette expérience. Si READ_COMMITTED_SNAPSHOT est activé, elle peut au contraire lire la version précédemment validée. Vérifiez cette option avant d'interpréter le comportement observé. Une lecture immédiate ne démontre pas que NOLOCK était nécessaire.
Le problème devient moins visible dans une jointure entre plusieurs tables. Un solde peut provenir d'une étape de la transaction et ses écritures associées d'une autre étape. Des modifications physiques concurrentes peuvent également conduire à manquer des lignes ou à les lire plusieurs fois. Une seconde exécution correcte ne rend pas le premier résultat acceptable. Cette intermittence explique pourquoi ces anomalies passent souvent pour des erreurs de l'outil de reporting.
Identifier ce qui faisait attendre
NOLOCK conserve les verrous de stabilité du schéma nécessaires à la compilation et à l'exécution. Une modification du schéma peut donc bloquer la requête, tandis qu'une longue requête peut retarder une modification du schéma. Le hint ne supprime ni le travail CPU, ni les débordements de tri, ni les lectures disque, ni le transfert réseau. Examinez la véritable catégorie d'attente.
Capturez le lecteur, le bloqueur principal, leurs instructions et l'âge de la transaction. Une transaction maintenue ouverte pendant un appel externe demande une correction du code applicatif. Un parcours lisant des millions de lignes inutiles demande une révision des filtres et de l'indexation. Un tableau de bord parcourant toute l'histoire peut justifier un agrégat rafraîchi séparément. Ces réponses traitent des causes différentes.
Définissez ensuite le contrat de cohérence du rapport. Une vue validée par instruction suffit-elle ? Plusieurs instructions doivent-elles observer le même état ? Un instantané explicitement ancien convient-il ? READ_COMMITTED_SNAPSHOT et SNAPSHOT ne fournissent pas le même périmètre de cohérence. Une réplique lisible peut présenter du retard et ne rend pas automatiquement cohérent un traitement composé de plusieurs lectures.
Organiser le remplacement
Commencez avec un rapport dont le résultat peut être vérifié indépendamment. Retirez le hint dans un environnement représentatif, mesurez la durée et les lectures logiques, puis introduisez un écrivain concurrent. Si vous envisagez le versionnement, évaluez l'espace nécessaire aux versions, les lecteurs prolongés et les transactions d'écriture existantes. Une option à l'échelle de la base affecte davantage qu'un seul rapport.
Le test d'acceptation doit répéter l'expérience du solde et vérifier qu'aucune valeur non validée n'est publiée. Ajoutez une transaction sur plusieurs tables qui reproduit l'ordre réel des écritures de l'application. Contrôlez un invariant métier, par exemple la correspondance entre un total de facture et ses lignes. Un code HTTP 200 ne prouve rien sur cet invariant.
Conservez les résultats de performance et de correction séparément. Le remplacement est réussi quand il fournit une vue validée acceptable sous activité concurrente tout en respectant le délai du rapport. Terminez explicitement la transaction de démonstration par ROLLBACK et supprimez ensuite la table d'exercice. NOLOCK doit rester une concession consciente et limitée, jamais une habitude invisible dans toutes les requêtes.
Références techniques: Microsoft Learn: Table hints · Microsoft Learn: Transaction isolation.