Bu yanıt asıl soruya yardımcı olabilir, ancak öncelikle diğer yayınlardaki yanlış bilgileri ele almaktır. Ayrıca BOL'da saçmalık bölümünü vurgulamaktadır.
INSERT belgelerinde belirtildiği gibi , masaya özel bir kilit alacak. Tabloya karşı bir SELECT yapmanın tek yolu NOLOCK kullanmak veya işlemin yalıtım seviyesini ayarlamaktır.
BOL'un bağlantılı bölümünde şunlar bulunur:
INSERT ifadesi her zaman değiştirdiği tabloda özel (X) bir kilit alır ve işlem tamamlanıncaya kadar bu kilidi tutar. Özel (X) kilit ile başka hiçbir işlem verileri değiştiremez; okuma işlemleri sadece NOLOCK ipucu kullanılarak veya okunmamış izolasyon seviyesinin okunması ile gerçekleştirilebilir. Daha fazla bilgi için bkz . Veritabanı Altyapısında Kilitleme .
Not: 2014-8-27 itibariyle BOL, yukarıda belirtilen yanlış ifadeleri kaldıracak şekilde güncellenmiştir.
Neyse ki durum böyle değil. Bu şekilde bir tabloya eklemeler seri olarak gerçekleşir ve ekleme işlemi tamamlanıncaya kadar tüm okuyucular tüm tablodan engellenir. Bu, SQL Server'ı NTFS kadar verimli bir veritabanı sunucusu yapar. Çok değil.
Sağduyu, böyle olamayacağını gösterir, ancak Paul Randall'ın işaret ettiği gibi, " Kendinize bir iyilik yapın, kimseye güvenmeyin ". BOL dahil kimseye güvenemezsen, sanırım bunu kanıtlamamız gerekecek.
Bir veritabanı oluşturun ve kukla bir tabloyu bir grup satırla doldurun ve DatabaseId'in döndüğünü not edin.
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
Lock: acquired and lock: release olaylarını izleyecek, önceki komut dosyasından DatabaseId üzerinde filtreleme, dosya için bir yol belirleme ve TraceId öğesinin döndürüldüğünü belirten bir profil izleme ayarlayın.
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
Bir satır ekleyin ve izlemeyi durdurun:
USE LockDemo
GO
INSERT MyTable DEFAULT VALUES
GO
EXEC sp_trace_setstatus 3, 0
EXEC sp_trace_setstatus 3, 2
GO
İzleme dosyasını açın ve aşağıdakileri bulmalısınız:

Alınan kilitlerin sırası:
- MyTable'da Amaç Özel kilit
- Sayfa 1: 211'de Kasıtlı Özel kilit
- Eklenen değerin kümelenmiş dizin girişindeki RangeInsert-NullResource
- Anahtar üzerinde özel kilit
Kilitler daha sonra ters sırada serbest bırakılır. Hiçbir noktada masaya özel bir kilit alınmamıştır.
Ama bu sadece bir parti ekleme! Paralel çalışan iki, üç veya düzinelerce aynı şey değil.
Evet öyle. SQL Server'ın (ve muhtemelen herhangi bir ilişkisel veritabanı motorunun), bir ifadeyi ve / veya toplu işlemi işlerken diğer toplu işlerin çalışabileceği konusunda bir öngörü yoktur, bu nedenle kilit toplama sırası değişmez.
Serializable gibi daha yüksek izolasyon seviyeleri ne olacak?
Bu özel örnek için tam olarak aynı kilitler alınır. Bana güvenme, dene!