Warum "Transaktion starten", bevor "Abfrage einfügen" die gesamte Tabelle sperrt?


11

Ich verwende SQL Server 2005 Express.

In einem Szenario habe ich einen Begin TransactionBefehl kurz vor einem hinzugefügtINSERT Anweisung in einer gespeicherten Prozedur . Als ich diese gespeicherte Prozedur ausführte, sperrte sie die gesamte Tabelle und alle gleichzeitigen Verbindungen zeigten bis zu diesem Zeitpunkt eine blockierte Anzeige INSERT.

Warum wird die gesamte Tabelle gesperrt und wie kann ich dieses Problem in SQL Server 2005 Express beheben?

Bearbeitet

Die Abfrage ist wie folgt:

INSERT INTO <table2> SELECT * FROM <table1> WHERE table1.workCompleted = 'NO'

2
Es sperrt die Tabelle nicht in postgresql.
Scott Marlowe

Benötigen Sie mehr @RPK. Mit der Tabelle DDL und einem Beispiel der Einfügungen können wir Ihnen eine genaue Erklärung geben, was passiert. Ohne sie raten wir nur.
Mark Storey-Smith

Diese Frage ist zu vage. Ich entferne alle Verweise auf andere DBMS und beschränke die Antworten auf SqlServer. Wenn das OP oder ein anderer Leser die Vorzüge dieses Kernkonzepts auf anderen Plattformen diskutieren möchte, sollten wir es einmal pro Plattform diskutieren lassen. Es ist nachteilig, dies zu einem kartesischen Join zu machen, da es auf einer Seite zu viele verschiedene Gesprächsthemen gibt.
Jcolebrand

Antworten:


25

Diese Antwort kann sich als hilfreich für die ursprüngliche Frage erweisen, dient jedoch in erster Linie dazu, ungenaue Informationen in anderen Posts zu behandeln. Es wird auch ein Abschnitt des Unsinns in BOL hervorgehoben.

Und wie in der INSERT- Dokumentation angegeben, erhält es eine exklusive Sperre für den Tisch. Die einzige Möglichkeit, ein SELECT für die Tabelle durchzuführen, besteht darin, NOLOCK zu verwenden oder die Isolationsstufe der Transaktion festzulegen.

Der verknüpfte Abschnitt von BOL lautet:

Eine INSERT-Anweisung erhält immer eine exklusive (X) Sperre für die Tabelle, die sie ändert, und hält diese Sperre, bis die Transaktion abgeschlossen ist. Mit einer exklusiven (X) Sperre können keine anderen Transaktionen Daten ändern. Lesevorgänge können nur unter Verwendung des NOLOCK-Hinweises oder der nicht festgeschriebenen Isolationsstufe erfolgen. Weitere Informationen finden Sie unter Sperren im Datenbankmodul .

NB: Ab dem 27.08.2014 wurde BOL aktualisiert, um die oben angegebenen falschen Aussagen zu entfernen.

Zum Glück ist dies nicht der Fall. Wenn dies der Fall wäre, würden Einfügungen in eine Tabelle seriell erfolgen und alle Leser würden für die gesamte Tabelle blockiert, bis die Einfügetransaktion abgeschlossen ist. Das würde SQL Server als Datenbankserver genauso effizient machen wie NTFS. Nicht sehr.

Der gesunde Menschenverstand legt nahe, dass dies nicht möglich ist, aber wie Paul Randall betont: " Tun Sie sich selbst einen Gefallen, vertrauen Sie niemandem ". Wenn Sie niemandem vertrauen können, einschließlich BOL , müssen wir es wohl nur beweisen.

Erstellen Sie eine Datenbank und füllen Sie eine Dummy-Tabelle mit einer Reihe von Zeilen, wobei Sie die zurückgegebene DatabaseId notieren.

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;

USE [master]
GO

IF EXISTS (SELECT name FROM sys.databases WHERE name = N'LockDemo')
DROP DATABASE [LockDemo]
GO

DECLARE @DataFilePath NVARCHAR(4000)
SELECT 
    @DataFilePath = SUBSTRING(physical_name, 1, CHARINDEX(N'master.mdf', LOWER(physical_name)) - 1)
FROM 
    master.sys.master_files
WHERE 
    database_id = 1 AND file_id = 1

EXEC ('
CREATE DATABASE [LockDemo] ON  PRIMARY 
( NAME = N''LockDemo'', FILENAME = N''' + @DataFilePath + N'LockDemo.mdf' + ''', SIZE = 2MB , MAXSIZE = UNLIMITED, FILEGROWTH = 2MB )
 LOG ON 
( NAME = N''LockDemo_log'', FILENAME = N''' + @DataFilePath + N'LockDemo_log.ldf' + ''', SIZE = 1MB , MAXSIZE = UNLIMITED , FILEGROWTH = 1MB )
')

GO

USE [LockDemo]
GO

SELECT DB_ID() AS DatabaseId

CREATE TABLE [dbo].[MyTable]
(
    [id] [int] IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , [filler] CHAR(4030) NOT NULL DEFAULT REPLICATE('A', 4030) 
)
GO

INSERT MyTable DEFAULT VALUES;
GO 100

Richten Sie einen Profiler-Trace ein, der die Ereignisse sperren: erfasst und gesperrt: freigegeben verfolgt, nach der Datenbank-ID aus dem vorherigen Skript filtert, einen Pfad für die Datei festlegt und die zurückgegebene Trace-ID notiert.

declare @rc int
declare @TraceID int
declare @maxfilesize BIGINT
declare @databaseid INT
DECLARE @tracefile NVARCHAR(4000)

set @maxfilesize = 5 
SET @tracefile = N'D:\Temp\LockTrace'
SET @databaseid = 9

exec @rc = sp_trace_create @TraceID output, 0, @tracefile, @maxfilesize, NULL 
if (@rc != 0) goto error

declare @on bit
set @on = 1
exec sp_trace_setevent @TraceID, 24, 32, @on
exec sp_trace_setevent @TraceID, 24, 1, @on
exec sp_trace_setevent @TraceID, 24, 57, @on
exec sp_trace_setevent @TraceID, 24, 3, @on
exec sp_trace_setevent @TraceID, 24, 51, @on
exec sp_trace_setevent @TraceID, 24, 12, @on
exec sp_trace_setevent @TraceID, 60, 32, @on
exec sp_trace_setevent @TraceID, 60, 57, @on
exec sp_trace_setevent @TraceID, 60, 3, @on
exec sp_trace_setevent @TraceID, 60, 51, @on
exec sp_trace_setevent @TraceID, 60, 12, @on
exec sp_trace_setevent @TraceID, 23, 32, @on
exec sp_trace_setevent @TraceID, 23, 1, @on
exec sp_trace_setevent @TraceID, 23, 57, @on
exec sp_trace_setevent @TraceID, 23, 3, @on
exec sp_trace_setevent @TraceID, 23, 51, @on
exec sp_trace_setevent @TraceID, 23, 12, @on

-- DatabaseId filter
exec sp_trace_setfilter @TraceID, 3, 0, 0, @databaseid

-- Set the trace status to start
exec sp_trace_setstatus @TraceID, 1

-- display trace id for future references
select TraceID=@TraceID
goto finish

error: 
select ErrorCode=@rc

finish: 
go

Fügen Sie eine Zeile ein und stoppen Sie die Ablaufverfolgung:

USE LockDemo
GO
INSERT MyTable DEFAULT VALUES
GO
EXEC sp_trace_setstatus 3, 0
EXEC sp_trace_setstatus 3, 2
GO

Öffnen Sie die Trace-Datei und Sie sollten Folgendes finden:

Profiler-Fenster

Die Reihenfolge der gesperrten Sperren ist:

  1. Absichts-exklusive Sperre für MyTable
  2. Absichts-Exklusiv-Sperre auf Seite 1: 211
  3. RangeInsert-NullResource für den Clustered-Indexeintrag für den einzufügenden Wert
  4. Exklusives Schlüsselschloss

Die Schlösser werden dann in umgekehrter Reihenfolge freigegeben. Zu keinem Zeitpunkt wurde eine exklusive Sperre für den Tisch erworben.

Dies ist jedoch nur eine Chargeneinfügung! Das ist nicht dasselbe wie zwei, drei oder Dutzende, die parallel laufen.

Ja ist es. SQL Server (und möglicherweise jedes relationale Datenbankmodul) hat keine Voraussicht darüber, welche anderen Stapel möglicherweise ausgeführt werden, wenn eine Anweisung und / oder ein Stapel verarbeitet werden, sodass die Reihenfolge der Sperrenerfassung nicht variiert.

Was ist mit höheren Isolationsstufen, z. B. serialisierbar?

Für dieses spezielle Beispiel werden genau die gleichen Sperren verwendet. Vertrau mir nicht, versuch es!


2
Sehr informativ. Gute Arbeit @Mark!
Jcolebrand

0

Ich mache nicht viel T-SQL-Arbeit, aber ich lese die Dokumentation ...

Dies ist beabsichtigt, wie in der BEGIN TRANSACTION angegeben :

Abhängig von den aktuellen Einstellungen für die Transaktionsisolationsstufe werden viele Ressourcen, die zur Unterstützung der von der Verbindung ausgegebenen Transact-SQL-Anweisungen erworben wurden, von der Transaktion gesperrt, bis sie entweder mit einer COMMIT TRANSACTION- oder einer ROLLBACK TRANSACTION-Anweisung abgeschlossen wird.

Und wie in der INSERT- Dokumentation angegeben, erhält es eine exklusive Sperre für den Tisch. Die einzige Möglichkeit, ein SELECT für die Tabelle NOLOCKdurchzuführen, besteht darin , die Isolationsstufe der Transaktion zu verwenden oder festzulegen.


4
Hatte diese ziemlich schlecht formulierte Aussage in BOL noch nie bemerkt. Eine exklusive Sperre für etwas innerhalb der Ressourcenhierarchie ist erforderlich, aber es ist definitiv nicht immer die Tabelle.
Mark Storey-Smith

6
-1 für die Dokumente (nicht Ihre Schuld) - Es ist leicht zu beweisen, dass dies bei der Snapshot-Isolation nicht der Fall ist, sodass die Decke "immer eine exklusive (X) -Sperre erwirbt" falsch ist. Nicht sicher über andere Isolationsstufen.
Jack sagt, versuchen Sie topanswers.xyz
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.