it-swarm.asia

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

Saya menggunakan SQL Server 2005 Express.

Dalam sebuah skenario, saya menambahkan Begin Transaction perintah tepat sebelum pernyataan INSERT dalam prosedur tersimpan. Ketika saya menjalankan prosedur tersimpan ini, itu mengunci seluruh tabel dan semua koneksi bersamaan menunjukkan tampilan yang digantung sampai waktu INSERT ini selesai.

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'
11
RPK

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 MASUKKAN , ia 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 memperoleh 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: Mulai 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 menyarankan hal itu tidak mungkin terjadi, tetapi seperti yang ditunjukkan oleh Paul Randall, " Bantulah dirimu sendiri, jangan percaya siapa pun " Jika Anda tidak dapat mempercayai siapa pun, termasuk BOL , saya kira kita 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 [email protected]
goto finish

error: 
select [email protected]

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:

Profiler window

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 mis. Serializable?

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

25

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

Ini adalah 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 MASUKKAN , ia akan mendapatkan kunci eksklusif di atas meja. Satu-satunya cara SELECT dapat dilakukan terhadap tabel adalah dengan menggunakan NOLOCK atau mengatur tingkat isolasi transaksi.

0
user507