Этот ответ может оказаться полезным для первоначального вопроса, но в первую очередь для устранения неточной информации в других сообщениях. Это также выдвигает на первый план раздел ерунды в BOL.
И, как указано в документации INSERT , он получит эксклюзивную блокировку на столе. Единственный способ сделать SELECT для таблицы - это использовать NOLOCK или установить уровень изоляции транзакции.
Связанный раздел BOL гласит:
Оператор INSERT всегда получает монопольную блокировку (X) для таблицы, которую он изменяет, и удерживает эту блокировку до завершения транзакции. С эксклюзивной блокировкой (X) никакие другие транзакции не могут изменять данные; Операции чтения могут выполняться только с использованием подсказки NOLOCK или считывания незафиксированного уровня изоляции. Для получения дополнительной информации см. Блокировка в компоненте Database Engine .
Примечание: по состоянию на 2014-8-27 BOL был обновлен, чтобы удалить неверные утверждения, приведенные выше.
К счастью, это не так. Если бы это было так, вставки в таблицу происходили бы последовательно, и все считыватели были бы заблокированы из всей таблицы до завершения транзакции вставки. Это сделало бы SQL Server таким же эффективным сервером баз данных, как NTFS. Не очень.
Здравый смысл подсказывает, что этого не может быть, но, как указывает Пол Рэндалл: « Сделай себе одолжение, никому не доверяй ». Если вы не можете никому доверять, включая BOL , я думаю, нам просто нужно это доказать.
Создайте базу данных и заполните фиктивную таблицу кучей строк, отметив возвращенный 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
Настройте трассировку профилировщика, которая будет отслеживать события «зафиксировано: получено» и «заблокировано: освобождено», отфильтрована по DatabaseId из предыдущего сценария, задана путь к файлу и отмечен возвращенный 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
Вставьте строку и остановите трассировку:
USE LockDemo
GO
INSERT MyTable DEFAULT VALUES
GO
EXEC sp_trace_setstatus 3, 0
EXEC sp_trace_setstatus 3, 2
GO
Откройте файл трассировки, и вы должны найти следующее:
Последовательность принятых блокировок:
- Intent-Эксклюзивная блокировка на MyTable
- Intent-Эксклюзивный замок на странице 1: 211
- RangeInsert-NullResource для записи кластеризованного индекса для вставляемого значения
- Эксклюзивный замок на ключ
Замки затем отпускаются в обратном порядке. Ни в коем случае не было эксклюзивной блокировки на столе.
Но это только одна партия вставки! Это не то же самое, что два, три или десятки, работающие параллельно.
Да, именно так. SQL Server (и, возможно, любой механизм реляционных баз данных) не имеет представления о том, какие другие пакеты могут выполняться при обработке оператора и / или пакета, поэтому последовательность получения блокировки не меняется.
Как насчет более высоких уровней изоляции, например, Serializable?
Для этого конкретного примера взяты те же самые блокировки. Не верь мне, попробуй!