Mengapa "Mulai Transaksi" sebelum "Sisipkan Kueri" mengunci seluruh tabel?

11

Saya menggunakan SQL Server 2005 Express.

Dalam skenario, saya menambahkan Begin Transactionperintah tepat sebelum INSERTpernyataan dalam prosedur tersimpan. Ketika saya menjalankan prosedur tersimpan ini, itu mengunci seluruh tabel dan semua koneksi bersamaan menunjukkan tampilan digantung sampai saat ini INSERTselesai.

Mengapa seluruh tabel terkunci dan bagaimana cara mengatasi masalah ini di SQL Server 2005 Express?

Diedit

Kueri adalah sebagai berikut:

INSERT INTO <table2> SELECT * FROM <table1> WHERE table1.workCompleted = 'NO'
RPK
sumber
2
Itu tidak mengunci tabel di postgresql.
Scott Marlowe
Perlu lebih banyak @RPK. Dengan tabel DDL dan contoh sisipan, kami dapat memberikan Anda penjelasan yang akurat tentang apa yang terjadi. Tanpa itu kita hanya menebak.
Mark Storey-Smith
Pertanyaan ini terlalu kabur. Saya menghapus referensi ke DBMS lain dan membatasi respons ke SqlServer. Jika OP atau pembaca lain ingin mendiskusikan manfaat konsep inti ini pada platform lain, maka kita harus membahasnya sekali per platform. Sangat merugikan untuk membuat ini menjadi cartesian bergabung, akan ada terlalu banyak utas percakapan di satu halaman.
jcolebrand

Jawaban:

25

Jawaban ini mungkin terbukti membantu untuk pertanyaan awal tetapi terutama untuk mengatasi informasi yang tidak akurat di pos lain. Ini juga menyoroti bagian omong kosong di BOL.

Dan seperti yang dinyatakan untuk dokumentasi INSERT , itu akan mendapatkan kunci eksklusif di atas meja. Satu-satunya cara SELECT dapat dilakukan terhadap tabel adalah dengan menggunakan NOLOCK atau mengatur tingkat isolasi transaksi.

Bagian tertaut dari status BOL:

Pernyataan INSERT selalu mendapatkan kunci (X) eksklusif pada tabel yang diubahnya, dan menahan kunci itu sampai transaksi selesai. Dengan kunci (X) eksklusif, tidak ada transaksi lain yang dapat memodifikasi data; operasi baca hanya dapat dilakukan dengan menggunakan petunjuk NOLOCK atau membaca tingkat isolasi tanpa komitmen. Untuk informasi lebih lanjut, lihat Mengunci Mesin Basis Data .

NB: Pada 2014-8-27 BOL telah diperbarui untuk menghapus pernyataan yang salah yang dikutip di atas.

Untungnya ini bukan masalahnya. Jika sudah begitu sisipan ke tabel akan terjadi secara serial dan semua pembaca akan diblokir dari seluruh tabel sampai transaksi penyisipan selesai. Itu akan membuat SQL Server sebagai server database seefisien NTFS. Tidak terlalu.

Akal sehat menunjukkan hal itu tidak mungkin terjadi, tetapi seperti yang ditunjukkan oleh Paul Randall, " Tolong, jangan percayai siapa pun ". Jika Anda tidak dapat mempercayai siapa pun, termasuk BOL , saya kira kita hanya harus membuktikannya.

Buat database dan isi tabel dummy dengan banyak baris, mencatat DatabaseId dikembalikan.

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

Menyiapkan pelacakan profiler yang akan melacak kunci: diperoleh dan mengunci: peristiwa yang dirilis, memfilter pada DatabaseId dari skrip sebelumnya, mengatur path untuk file dan mencatat TraceId yang dikembalikan.

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

Masukkan baris dan hentikan jejak:

USE LockDemo
GO
INSERT MyTable DEFAULT VALUES
GO
EXEC sp_trace_setstatus 3, 0
EXEC sp_trace_setstatus 3, 2
GO

Buka file jejak dan Anda harus menemukan yang berikut:

Jendela profiler

Urutan kunci yang diambil adalah:

  1. Kunci Intent-Eksklusif di MyTable
  2. Kunci Intent-Eksklusif di halaman 1: 211
  3. RangeInsert-NullResource pada entri indeks berkerumun untuk nilai yang dimasukkan
  4. Kunci eksklusif pada kunci

Kunci kemudian dilepaskan dalam urutan terbalik. Pada titik tidak ada kunci eksklusif diperoleh di atas meja.

Tapi ini hanya memasukkan satu batch! Itu tidak sama dengan dua, tiga atau puluhan yang berjalan secara paralel.

Ya itu. SQL Server (dan bisa dibilang mesin basis data relasional) tidak memiliki pandangan ke depan untuk apa batch lain dapat berjalan ketika memproses pernyataan dan / atau batch, sehingga urutan akuisisi kunci tidak bervariasi.

Bagaimana dengan tingkat isolasi yang lebih tinggi misalnya Serializable?

Untuk contoh khusus ini, kunci yang sama diambil. Jangan percaya padaku, coba!

Mark Storey-Smith
sumber
2
Sangat informatif. Kerja bagus @Mark!
jcolebrand
0

Saya tidak melakukan banyak pekerjaan T-SQL tetapi dari membaca dokumentasi ...

Ini dengan desain, sebagaimana dinyatakan dalam TRANSAKSI BEGIN :

Bergantung pada pengaturan level isolasi transaksi saat ini, banyak sumber daya yang diperoleh untuk mendukung pernyataan Transact-SQL yang dikeluarkan oleh koneksi dikunci oleh transaksi sampai diselesaikan dengan pernyataan TRANSAKSI KOMIT atau TRANSAKSI ROLLBACK.

Dan seperti yang dinyatakan untuk dokumentasi INSERT , itu akan mendapatkan kunci eksklusif di atas meja. Satu-satunya cara SELECT dapat dilakukan terhadap tabel adalah dengan menggunakan NOLOCKatau mengatur tingkat isolasi transaksi.


sumber
4
Tidak memperhatikan pernyataan yang agak buruk di BOL sebelumnya. Kunci eksklusif pada sesuatu dalam hirarki sumber daya akan diperlukan tetapi yang paling pasti tidak selalu tabel.
Mark Storey-Smith
6
-1 untuk dokumen (bukan kesalahan Anda) - mudah untuk membuktikan ini tidak benar dalam isolasi snapshot sehingga selimut "selalu mendapatkan kunci (X) eksklusif" salah. Tidak yakin tentang tingkat isolasi lainnya.
Jack bilang coba topanswers.xyz