Ця відповідь може виявитися корисною для початкового запитання, але насамперед стосується недостовірної інформації в інших публікаціях. Він також висвітлює розділ дурниць у BOL.
І як зазначено в документації INSERT , вона придбає ексклюзивний замок на столі. Єдиний спосіб зробити SELECT проти таблиці - це використовувати NOLOCK або встановити рівень ізоляції транзакції.
Пов'язаний розділ BOL говорить:
Оператор INSERT завжди набуває ексклюзивного (X) блокування на таблиці, яку він модифікує, і тримає цей замок до завершення транзакції. За допомогою ексклюзивного блокування (X) жодна інша транзакція не може змінювати дані; Операції зчитування можуть відбуватися лише за допомогою підказки NOLOCK або зчитування невдалого рівня ізоляції. Для отримання додаткової інформації див. Блокування в Механізмі бази даних .
Примітка: Станом на 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
Відкрийте файл трасування і вам слід знайти таке:
Послідовність взятих замків:
- Ексклюзивний замок на MyTable
- Блокування з намірною ексклюзивністю на сторінці 1: 211
- RangeInsert-NullResource на кластерному записі індексу для введеного значення
- Ексклюзивний замок на ключі
Потім замки відпускаються у зворотному порядку. Жодного разу на столі не придбали ексклюзивний замок.
Але це лише одна партійна вставка! Це не те саме, що два, три чи десятки, що працюють паралельно.
Так. Сервер SQL (і, мабуть, будь-який механізм реляційних баз даних) не має передбачення щодо того, які інші партії можуть бути запущені при обробці оператора та / або партії, тому послідовність придбання блокування не змінюється.
Що з вищими рівнями ізоляції, наприклад, серіалізацією?
Для цього конкретного прикладу взяті точно такі ж замки. Не вірте мені, спробуйте!