Essa resposta pode ser útil para a pergunta original, mas é principalmente para abordar informações imprecisas em outras postagens. Também destaca uma seção de bobagens no BOL.
E, como indicado na documentação do INSERT , ele adquirirá um bloqueio exclusivo na mesa. A única maneira de selecionar um SELECT na tabela é usar NOLOCK ou definir o nível de isolamento da transação.
A seção vinculada da BOL declara:
Uma instrução INSERT sempre adquire um bloqueio (X) exclusivo na tabela que modifica e mantém esse bloqueio até a transação ser concluída. Com um bloqueio exclusivo (X), nenhuma outra transação pode modificar dados; as operações de leitura podem ocorrer apenas com o uso da dica NOLOCK ou com o nível de isolamento não confirmado da leitura. Para mais informações, consulte Bloqueio do Database Engine .
NB: A partir de 27/08/2014, o BOL foi atualizado para remover as instruções incorretas citadas acima.
Felizmente, este não é o caso. Se assim fosse, as inserções em uma tabela ocorreriam em série e todos os leitores seriam bloqueados de toda a tabela até a transação de inserção ser concluída. Isso tornaria o SQL Server um servidor de banco de dados tão eficiente quanto o NTFS. Não muito.
O senso comum sugere que não pode ser assim, mas como Paul Randall aponta: " Faça um favor a si mesmo, não confie em ninguém ". Se você não pode confiar em ninguém, incluindo o BOL , acho que teremos que provar isso.
Crie um banco de dados e preencha uma tabela fictícia com várias linhas, observando o DatabaseId retornado.
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
Configure um rastreamento do criador de perfil que rastreie os eventos lock: adquirido e lock: liberado, filtrando o DatabaseId a partir do script anterior, definindo um caminho para o arquivo e observando o TraceId retornado.
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
Insira uma linha e pare o rastreio:
USE LockDemo
GO
INSERT MyTable DEFAULT VALUES
GO
EXEC sp_trace_setstatus 3, 0
EXEC sp_trace_setstatus 3, 2
GO
Abra o arquivo de rastreamento e você deve encontrar o seguinte:
A sequência de bloqueios efetuados é:
- Bloqueio Intent-Exclusive no MyTable
- Bloqueio de intenção-exclusivo na página 1: 211
- RangeInsert-NullResource na entrada de índice em cluster para o valor que está sendo inserido
- Fechadura exclusiva na chave
Os bloqueios são liberados na ordem inversa. Em nenhum momento um bloqueio exclusivo foi adquirido na mesa.
Mas esta é apenas uma inserção de lote! Não é o mesmo que duas, três ou dezenas correndo paralelamente.
Sim, ele é. O SQL Server (e possivelmente qualquer mecanismo de banco de dados relacional) não tem previsão de quais outros lotes podem estar em execução ao processar uma instrução e / ou lote, portanto a sequência de aquisição do bloqueio não varia.
E quanto aos níveis mais altos de isolamento, por exemplo, serializável?
Neste exemplo em particular, exatamente os mesmos bloqueios são feitos. Não confie em mim, tente!