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:
Urutan kunci yang diambil adalah:
- Kunci Intent-Eksklusif di MyTable
- Kunci Intent-Eksklusif di halaman 1: 211
- RangeInsert-NullResource pada entri indeks berkerumun untuk nilai yang dimasukkan
- 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!