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 stwierdzono 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: W dniu 2014–8–27 BOL został zaktualizowany w celu usunięcia niepoprawnych stwierdzeń cytowanych 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 nikomu ”. Jeśli nie możesz ufać nikomu, w tym BOL , chyba 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 blokowanie: 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 TraceID=@TraceID
goto finish
error:
select ErrorCode=@rc
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:
Sekwencja wykonanych blokad jest następująca:
- Blokada celowa w MyTable
- Blokada celowa na stronie 1: 211
- RangeInsert-NullResource na pozycji indeksu klastrowego dla wstawianej wartości
- Ekskluzywny zamek na klucz
Blokady są następnie zwalniane w odwrotnej kolejności. W żadnym momencie nie uzyskano wyłącznego zamka 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. Serializable?
W tym konkretnym przykładzie przyjmowane są dokładnie takie same blokady. Nie ufaj mi, spróbuj!