Neden "Sorgu Ekle" tüm tabloyu kilitlemeden önce "İşlemi Başlat"?


11

SQL Server 2005 Express kullanıyorum.

Bir senaryoda, saklı yordamdaki bir deyimden Begin Transactionhemen önce komut ekledim INSERT. Bu saklı yordamı yürüttüğümde, tüm tabloyu kilitledi ve tüm eşzamanlı bağlantılar, bu bitene kadar asılı ekran gösterdi INSERT.

Neden tüm tablo kilitli ve SQL Server 2005 Express'te bu sorunun üstesinden nasıl gelebilirim?

Düzenlenen

Sorgu aşağıdaki gibidir:

INSERT INTO <table2> SELECT * FROM <table1> WHERE table1.workCompleted = 'NO'

2
Postgresql'de tabloyu kilitlemez.
Scott Marlowe

Daha fazla @RPK gerekiyor. DDL tablosu ve kesici uçların bir örneği ile size neler olduğunu doğru bir şekilde açıklayabiliriz. Onsuz sadece tahmin ediyoruz.
Mark Storey-Smith

Bu soru çok belirsiz. Diğer DBMS herhangi bir başvuru kaldırma ve yanıtları SqlServer için sınırlandırıyorum. OP veya başka bir okuyucu bu temel kavramın avantajlarını diğer platformlarda tartışmak istiyorsa, platform başına bir kez tartışmalıyız. Bunu kartezyen birleştirme yapmak zararlıdır, bir sayfada çok fazla farklı konuşma dizisi olacaktır.
jcolebrand

Yanıtlar:


25

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:

Profil oluşturucu penceresi

Alınan kilitlerin sırası:

  1. MyTable'da Amaç Özel kilit
  2. Sayfa 1: 211'de Kasıtlı Özel kilit
  3. Eklenen değerin kümelenmiş dizin girişindeki RangeInsert-NullResource
  4. 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!


2
Çok bilgilendirici. İyi iş @ Mark!
jcolebrand

0

Çok fazla T-SQL işi yapmıyorum ama belgeleri okurken ...

Bu, BAŞLANGIÇ İŞLEMİ'nde belirtildiği gibi tasarım gereğidir :

Geçerli işlem yalıtım düzeyi ayarlarına bağlı olarak, bağlantı tarafından yayınlanan Transact-SQL deyimlerini desteklemek için edinilen birçok kaynak bir COMMIT TRANSACTION veya ROLLBACK TRANSACTION deyimi ile tamamlanana kadar işlem tarafından kilitlenir.

INSERT belgelerinde belirtildiği gibi , masaya özel bir kilit alacak. Bir SELECT'in tabloya göre yapılabilmesinin tek yolu NOLOCKişlemin yalıtım seviyesini kullanmak veya ayarlamaktır.


4
Daha önce BOL'de oldukça kötü ifade edilen ifadeyi fark etmemişti. Kaynak hiyerarşisinde bir şey için özel bir kilit gerekecektir, ancak kesinlikle her zaman tablo değildir.
Mark Storey-Smith

6
Dokümanlar için -1 (sizin hatanız değil) - anlık görüntü izolasyonunda bunun doğru olmadığını kanıtlamak kolaydır, bu nedenle battaniye "her zaman özel (X) bir kilit alır" yanlıştır. Diğer izolasyon seviyelerinden emin değilim.
Jack diyor ki topanswers.xyz
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.