Questa risposta può rivelarsi utile alla domanda originale ma è principalmente indirizzata a informazioni inaccurate in altri post. Evidenzia anche una sezione di assurdità in BOL.
E come indicato per la documentazione INSERT , acquisirà un blocco esclusivo sul tavolo. L'unico modo in cui un SELECT può essere eseguito sulla tabella è utilizzare NOLOCK o impostare il livello di isolamento della transazione.
La sezione collegata di BOL afferma:
Un'istruzione INSERT acquisisce sempre un blocco esclusivo (X) sulla tabella che modifica e mantiene quel blocco fino al completamento della transazione. Con un blocco esclusivo (X), nessuna altra transazione può modificare i dati; le operazioni di lettura possono avvenire solo con l'uso del suggerimento NOLOCK o leggere il livello di isolamento senza commit. Per ulteriori informazioni, vedere Blocco nel motore di database .
NB: A partire dal 2014-8-27 BOL è stato aggiornato per rimuovere le dichiarazioni errate sopra citate.
Per fortuna non è così. Se così fosse, gli inserimenti in una tabella verrebbero eseguiti in serie e tutti i lettori verrebbero bloccati dall'intera tabella fino al completamento della transazione di inserimento. Ciò renderebbe SQL Server efficiente come server di database come NTFS. Non molto.
Il buon senso suggerisce che non può essere così, ma come sottolinea Paul Randall, " Fatti un favore, non fidarti di nessuno ". Se non puoi fidarti di nessuno, incluso BOL , credo che dovremo solo dimostrarlo.
Creare un database e popolare una tabella fittizia con un gruppo di righe, notando che DatabaseId restituito.
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
Imposta una traccia del profiler che seguirà il lock: acquisito e lock: eventi rilasciati, filtrando su DatabaseId dallo script precedente, impostando un percorso per il file e notando il ritorno di 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
Inserisci una riga e interrompi la traccia:
USE LockDemo
GO
INSERT MyTable DEFAULT VALUES
GO
EXEC sp_trace_setstatus 3, 0
EXEC sp_trace_setstatus 3, 2
GO
Apri il file di traccia e dovresti trovare quanto segue:
La sequenza dei blocchi acquisiti è:
- Blocco intento-esclusivo su MyTable
- Blocco intento-esclusivo nella pagina 1: 211
- RangeInsert-NullResource sulla voce dell'indice cluster per il valore da inserire
- Chiave di blocco esclusivo
I blocchi vengono quindi rilasciati in ordine inverso. In nessun momento è stato acquisito un blocco esclusivo sul tavolo.
Ma questo è solo un inserimento batch! Non è lo stesso di due, tre o dozzine che corrono in parallelo.
Sì. SQL Server (e probabilmente qualsiasi motore di database relazionale) non ha alcuna previsione su quali altri batch potrebbero essere in esecuzione quando elabora un'istruzione e / o un batch, quindi la sequenza di acquisizione dei blocchi non varia.
Che dire dei livelli di isolamento più elevati, ad esempio serializzabili?
Per questo esempio particolare vengono presi esattamente gli stessi blocchi. Non fidarti di me, provalo!