Was ist NOLOCK?
NOLOCK
ist ein sogenannter Abfragehinweis, mit dem ihr das Standardverhalten des SQL Server für eure Abfrage überschreiben oder verändern könnt. NOLOCK
ist ein Hinweis, der das Isolationslevel für die entsprechende Abfrage überschreibt. Das funktioniert nur für lesende Abfragen, ihr könnt den NOLOCK
-Hint also nicht in UPDATE
, DELETE
oder INSERT
-Statements verwenden. Der NOLOCK
-Hinweis ist dabei synonym mit dem READUNCOMMITED
-Hinweis, er ändert das Isolationslevel eurer Abfrage also dahingehend, dass „Dirty Reads“ möglich werden, die Abfrage also Daten zurück liefern kann, die am Ende durch einen ROLLBACK
gar nicht in der Tabelle landen.
Verwendung von NOLOCK
Um NOLOCK
ranken sich einige Mythen, gleichzeitig ist es vermutlich im SQL Server-Universum am häufigsten verwendete Abfragehinweis. In den folgenden Abfragen seht ihr die Effekte und Verwendung von NOLOCK
etwas näher erklärt. Um diese Abfragen nachvollziehen zu können, müsst ihr üblicherweise mit zwei Sessions gleichzeitig arbeiten, ihr braucht also mehrere offene Abfrage-Tabs in eurem Azure Data Studio oder SSMS oder anderen Abfragetool eurer Wahl.
Im ersten Tab startet ihr nun eine Transaktion und macht in dieser Transaktion ein UPDATE
auf einer Tabelle:
BEGIN TRANSACTION UPDATE [Warehouse].[StockItemTransactions] SET [TransactionTypeID] = 13 WHERE [TransactionTypeID] = 10
Beachtet, dass die Transaktion nicht beendet wird. Das bedeutet, wenn ihr nun in einer zweiten Abfrage die Tabelle abfragt, dann erhaltet ihr kein Ergebnis zurück. Öffnet also eine zweite Abfrage und führt dort folgende Abfrage aus:
SELECT [StockItemTransactionID] ,[StockItemID] ,[TransactionTypeID] ,[CustomerID] ,[InvoiceID] ,[SupplierID] ,[PurchaseOrderID] FROM [Warehouse].[StockItemTransactions] WHERE [TransactionTypeID] = 10
Hier könnt ihr lange warten, werdet jedoch keine Resultate sehen. Das ist natürlich so, weil im SQL Server READ COMMITTED
das standardmäßig ausgewählte Isolationslevel ist und ihr bei einer offenen Transaktion auf der Tabelle mit eurem Lesevorgang warten müsst. Das seht ihr zum Beispiel an den Ergebnissen der folgenden Abfrage:
DECLARE @who TABLE( SPID INT, Status VARCHAR(MAX), LOGIN VARCHAR(MAX), HostName VARCHAR(MAX), BlkBy VARCHAR(MAX), DBName VARCHAR(MAX), Command VARCHAR(MAX), CPUTime INT, DiskIO INT, LastBatch VARCHAR(MAX), ProgramName VARCHAR(MAX), SPID_1 INT, REQUESTID INT ) INSERT INTO @who EXEC sp_who2 SELECT [SPID] ,[Status] ,[LOGIN] ,[BlkBy] ,[DBName] ,[Command] FROM @who WHERE DBName = 'WideWorldImporters'
Hier seht ihr 2 Transaktionen, eine im Status „sleeping“ mit dem Kommando „AWAITING COMMAND“, das ist die noch offene Transaktion in der das UPDAT
E bereits gelaufen ist, und eine mit dem Status „SUSPENDED“ mit dem Kommando SELECT
, bei der in der Spalte „BlkBy“ die SPID der ersten Transaktion steht, die also von dieser geblockt wird.
Brecht ihr nun die SELECT
-Abfrage ab und fügt hinter den Tabellennamen den NOLOCK
-Hinweis hinzu, dann erhaltet ihr sofort Ergebnisse (bzw. in diesem Fall keine Ergebnisse, da ihr ja in der Transaktion den Typen 10 in 13 geändert hattet):
SELECT [StockItemTransactionID] ,[StockItemID] ,[TransactionTypeID] ,[CustomerID] ,[InvoiceID] ,[SupplierID] ,[PurchaseOrderID] FROM [Warehouse].[StockItemTransactions] (NOLOCK) WHERE [TransactionTypeID] = 10
Führt ihr nun im ersten Fenster ein ROLLBACK
aus und führt dann die Abfrage auf die Tabelle erneut aus, dann erhaltet ihr Ergebnisse, da die Transaktionen mit Typ 10 nun ja wieder zurückgesetzt wurden.
Hier erkennt ihr schon den ersten Effekt, den NOLOCK
hat: ihr habt eine UNCOMMITTED
Transaktion gelesen und damit Daten, die so nie in der Datenbank persistiert wurden.
Trotzdem ist NOLOCK
der mit Abstand am häufigsten verwendete Abfragehinweise im T-SQL Universum. Das liegt daran, dass sich hierum einige Mythen gebildet haben:
- Abfragen mit NOLOCK können nicht durch andere Operationen blockiert werden.
Das ist falsch. Um das zu sehen, führt die obigeNOLOCK
-Abfrage aus und während sie läuft gleichzeitig in einem anderen Tab sp_lock. Ihr seht dann Einträge für einen Schema-Lock auf der abgefragten Tabelle und für einen Shared-Lock auf der Datenbank. Das heißt eine Operation die das Schema der Tabelle ändert (beispielsweise eine Index-Erstellung oder einALTER TABLE
Kommando) kann eineNOLOCK
-Tabelle durchaus blockieren. - Abfragen mit NOLOCK blockieren keine anderen Operationen.
Das stimmt definitiv nicht. Durch den oben gezeigten Schema-Lock auf die Tabelle kann eineNOLOCK
-Abfrage natürlich auch eineINDEX
-Erstellung blockieren. - Wenn alle Abfragen mit NOLOCK laufen, wird alles schneller.
Das entspricht so nicht ganz der Wahrheit. Natürlich müssen Abfragen, die mitNOLOCK
laufen keine Daten ins Transaktions-Log legen und benötigen daher weniger Speicher und sind schneller. Trotzdem ist es wenig sinnvoll, auf Abfrageebene das Isolationslevel für jede Abfrage zu ändern, denn einerseits erhaltet ihr Resultate wieUNCOMMITED READS
, die vom DBA nicht vorgesehen sind (sonst hätte die Datenbank ja global ein anderesISOLATION LEVEL
). - Mach einfach NOLOCK, dann ist alles gut.
Wenn ihr das Standardverhalten des SQL Servers überschreibt, dann sollte euch bewusst sein, was ihr da tut und warum. Wenn ihrNOLOCK
standardmäßig nutzt, macht euch bewusst warum und was die Seiteneffekte sein könnten.