it-swarm.dev

Dlaczego „Rozpocznij transakcję” przed „Wstaw zapytanie” blokuje cały stół?

Korzystam z programu SQL Server 2005 Express.

W scenariuszu dodałem Begin Transaction polecenie tuż przed instrukcją INSERT w procedurze składowanej. Kiedy wykonałem tę procedurę przechowywaną, zablokowałem całą tabelę i wszystkie jednoczesne połączenia pokazywały zawieszony ekran do czasu zakończenia tego INSERT.

Dlaczego cała tabela jest blokowana i jak rozwiązać ten problem w SQL Server 2005 Express?

Edytowane

Zapytanie jest jak poniżej:

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

Ta odpowiedź może okazać się pomocna w stosunku do pierwotnego pytania, ale ma przede wszystkim dotyczyć niedokładnych informacji w innych postach. Podkreśla także część nonsensów w BOL.

I jak podano w dokumentacji INSERT , uzyska on wyłączną blokadę na stole. Jedynym sposobem, w jaki można dokonać SELECT względem tabeli, jest użycie NOLOCK lub ustawienie poziomu izolacji transakcji.

Połączona sekcja BOL stwierdza:

Instrukcja INSERT zawsze nabywa blokadę wyłączności (X) w tabeli, którą modyfikuje, i utrzymuje tę blokadę do czasu zakończenia transakcji. Dzięki blokadzie na wyłączność (X) żadne inne transakcje nie mogą modyfikować danych; operacje odczytu mogą odbywać się tylko przy użyciu podpowiedzi NOLOCK lub odczytu niezaangażowanego poziomu izolacji. Aby uzyskać więcej informacji, zobacz Blokowanie w silniku bazy danych .

Uwaga: Stan na dzień 2014–8–27 BOL został zaktualizowany, aby usunąć niepoprawne stwierdzenia cytowane powyżej.

Na szczęście tak nie jest. Gdyby tak było, wstawianie do tabeli występowałoby szeregowo, a wszystkie czytniki byłyby blokowane z całej tabeli, dopóki transakcja wstawiania nie zostanie zakończona. Dzięki temu SQL Server byłby tak samo wydajnym serwerem bazy danych jak NTFS. Nie bardzo.

Zdrowy rozsądek sugeruje, że tak nie może być, ale jak zauważa Paul Randall: „ Zrób sobie przysługę, nie ufaj nikom ”. Jeśli nie możesz komuś zaufać, w tym BOL , myślę, że będziemy musieli to udowodnić.

Utwórz bazę danych i wypełnij tabelę zastępczą wiązką wierszy, zwracając uwagę, że zwrócono DatabaseId.

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

Skonfiguruj śledzenie profilera, które będzie śledzić blokadę: uzyskane i zablokowanie: zwolnione zdarzenia, filtrowanie na DatabaseId z poprzedniego skryptu, ustawienie ścieżki do pliku i odnotowanie zwrócenia TraceId.

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 [email protected]
goto finish

error: 
select [email protected]

finish: 
go

Wstaw wiersz i zatrzymaj śledzenie:

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

Otwórz plik śledzenia, a znajdziesz następujące informacje:

Profiler window

Sekwencja wykonanych blokad jest następująca:

  1. Blokada celowa w MyTable
  2. Blokada celowa na stronie 1: 211
  3. RangeInsert-NullResource na pozycji indeksu klastrowego dla wstawianej wartości
  4. Ekskluzywny zamek na klucz

Blokady są następnie zwalniane w odwrotnej kolejności. W żadnym momencie nie uzyskano wyłączności blokady na stole.

Ale to tylko jedna wstawka partii! To nie to samo, co dwa, trzy lub kilkadziesiąt biegnących równolegle.

Tak to jest. SQL Server (i prawdopodobnie dowolny silnik relacyjnej bazy danych) nie ma przewidywania, jakie inne partie mogą być uruchomione podczas przetwarzania instrukcji i/lub partii, więc sekwencja pozyskiwania blokady nie jest różna.

Co z wyższymi poziomami izolacji np. Serializowalny?

W tym konkretnym przykładzie przyjmowane są dokładnie takie same blokady. Nie ufaj mi, spróbuj!

25

Nie wykonuję dużo pracy w języku T-SQL, ale czytam dokumentację ...

Jest to zgodne z projektem, jak podano w POCZĄTEK TRANSAKCJI :

W zależności od bieżących ustawień poziomu izolacji transakcji wiele zasobów uzyskanych w celu obsługi instrukcji Transact-SQL wydanych przez połączenie jest blokowanych przez transakcję, dopóki nie zostanie zakończona instrukcją COMMIT TRANSACTION lub ROLLBACK TRANSACTION.

I jak podano w dokumentacji INSERT , uzyska on wyłączną blokadę na stole. Jedynym sposobem, w jaki można wykonać WYBÓR na podstawie tabeli, jest użycie NOLOCK lub ustawienie poziomu izolacji transakcji.

0
user507