Kunci Eskalasi - Apa yang terjadi di sini?

139

Saat mengubah tabel (menghapus kolom) di SQL Server 2008, saya mengklik tombol Generate Change Script dan saya perhatikan bahwa skrip perubahan yang dihasilkannya menjatuhkan kolom, mengatakan "pergi" dan kemudian menjalankan pernyataan ALTER TABLE tambahan yang tampaknya disetel eskalasi kunci untuk tabel ke "TABLE". Contoh:

ALTER TABLE dbo.Contract SET (LOCK_ESCALATION = TABLE)

Saya juga harus mencatat bahwa ini adalah hal terakhir yang dilakukan skrip perubahan. Apa yang dilakukannya di sini dan mengapa ini menyetel LOCK_ESCALATION ke TABLE?

James Alexander
sumber

Jawaban:

169

" Lock Escalation " adalah cara SQL menangani penguncian untuk update besar. Ketika SQL akan mengubah banyak baris, lebih efisien bagi mesin database untuk mengambil lebih sedikit, kunci yang lebih besar (misalnya seluruh tabel) daripada mengunci banyak hal yang lebih kecil (misalnya kunci baris).

Tetapi ini bisa menjadi masalah ketika Anda memiliki tabel besar, karena mengunci seluruh tabel dapat mengunci kueri lain untuk waktu yang lama. Itulah kompromi: banyak kunci granularitas kecil lebih lambat daripada lebih sedikit (atau satu) kunci berbutir kasar, dan memiliki beberapa kueri yang mengunci bagian berbeda dari tabel menciptakan kemungkinan kebuntuan jika satu proses menunggu proses lainnya.

Ada opsi tingkat tabel LOCK_ESCALATION, baru di SQL 2008, yang memungkinkan kontrol eskalasi kunci. Defaultnya, "TABLE" memungkinkan kunci untuk meningkat hingga ke tingkat tabel. NONAKTIFKAN mencegah eskalasi lock ke seluruh tabel dalam banyak kasus. AUTO memungkinkan penguncian tabel kecuali jika tabel dipartisi, dalam hal ini penguncian hanya dibuat hingga tingkat partisi. Lihat posting blog ini untuk info lebih lanjut.

Saya menduga bahwa IDE menambahkan pengaturan ini saat membuat ulang tabel karena TABLE adalah default di SQL 2008. Perhatikan bahwa LOCK_ESCALATION tidak didukung di SQL 2005, jadi Anda harus menghapusnya jika mencoba menjalankan skrip pada 2005 contoh. Selain itu, karena TABLE adalah defaultnya, Anda dapat dengan aman menghapus baris tersebut saat menjalankan kembali skrip Anda.

Perhatikan juga bahwa, di SQL 2005 sebelum pengaturan ini ada, semua kunci bisa meningkat ke tingkat tabel-- dengan kata lain, "TABLE" adalah satu-satunya pengaturan di SQL 2005.

Justin Grant
sumber
1
Pos duplikat di Forum MSDN juga: social.msdn.microsoft.com/Forums/en-US/sqldatabaseengine/thread/…
Jonathan Kehayias
6
@dma_k - Opsi ini tidak relevan CREATE TABLEkarena tabel belum ada sehingga tidak ada yang perlu dikunci.
Justin Grant
1
Tetapi mengapa pernyataan LOCK_ESCALATION setelah pernyataan ALTER TABLE awal di skrip perubahan saat mendesain tabel di SSMS? Tentunya pada saat itu pekerjaan sudah selesai. Bukankah seharusnya sebelum mengubah struktur tabel?
Reversed Engineer
2
@DaveBoltman - SET adalah bagian dari pernyataan ALTER TABLE. Ini bukan pernyataan terpisah. Lihat docs.microsoft.com/en-us/sql/t-sql/statements/…
Justin Grant
2
JustinGrant, tetap saja, pertanyaan dari @DaveBoltman berdiri. Skrip yang dihasilkan SSMS untuk, misalnya, menambahkan kolom baru memiliki dua ALTER TABLEpernyataan terpisah . Pertama ALTER TABLE ADD column, lalu GO, lalu kedua ALTER TABLE SET LOCK_ESCALATION=TABLE, lalu kedua GO. Jadi, LOCK_ESCALATIONdiatur setelah kolom ditambahkan. Apa gunanya mengaturnya setelah fakta? Kedua ALTER TABLEpernyataan ini dibungkus dalam sebuah transaksi, tetapi tetap saja kolom ditambahkan sebelum LOCK_ESCALATIONditetapkan. Saya pikir saya akan menggali lebih jauh dan menulis jawaban lain.
Vladimir Baranov
12

Anda dapat memeriksa apakah Anda perlu menyertakan pernyataan LOCK_ESCALATION dalam skrip Anda dengan membandingkan nilai ini sebelum dan setelah menjalankan bagian utama skrip Anda:

SELECT lock_escalation_desc FROM sys.tables WHERE name='yourtablename'

Dalam kasus saya, mengubah tabel menjadi menjatuhkan atau menambah batasan tampaknya tidak mengubah nilai ini.

Bogdan Verbenets
sumber
12

Jawaban oleh Justin Grant menjelaskan apa yang LOCK_ESCALATIONdilakukan pengaturan secara umum, tetapi melewatkan satu detail penting dan tidak menjelaskan mengapa SSMS menghasilkan kode yang menetapkannya. Terutama, terlihat sangat aneh bahwa LOCK_ESCALATIONdisetel sebagai pernyataan terakhir dalam skrip.

Saya melakukan beberapa tes dan inilah pemahaman saya tentang apa yang terjadi di sini.

Versi pendek

The ALTER TABLEpernyataan yang menambahkan, tetes atau alter kolom implisit mengambil skema memodifikasi (SCH-M) kunci di atas meja, yang tidak ada hubungannya dengan LOCK_ESCALATIONpengaturan meja. LOCK_ESCALATIONmempengaruhi mengunci perilaku selama laporan DML ( INSERT, UPDATE, DELETE, dll), tidak selama laporan DDL ( ALTER). Kunci SCH-M selalu merupakan kunci dari seluruh objek database, tabel dalam contoh ini.

Sepertinya dari sinilah kebingungan itu berasal.

SSMS menambahkan ALTER TABLE <TableName> SET (LOCK_ESCALATION = ...)pernyataan tersebut ke skripnya dalam semua kasus, bahkan jika tidak diperlukan. Dalam kasus ketika pernyataan ini diperlukan, itu ditambahkan untuk mempertahankan pengaturan tabel saat ini, bukan untuk mengunci tabel dengan cara tertentu selama perubahan pada skema tabel yang terjadi dalam skrip itu.

Dengan kata lain, meja yang terkunci dengan kunci SCH-M pada pertama ALTER TABLE ALTER COLUMNpernyataan sementara semua pekerjaan mengubah skema tabel dilakukan. ALTER TABLE SET LOCK_ESCALATIONPernyataan terakhir tidak mempengaruhinya. Ini mempengaruhi hanya pernyataan DML masa depan ( INSERT, UPDATE, DELETE, dll) untuk meja itu.

Pada pandangan pertama, ini terlihat seolah-olah SET LOCK_ESCALATION = TABLEada hubungannya dengan fakta bahwa kami mengubah seluruh tabel (kami mengubah skema di sini), tetapi menyesatkan.

Versi panjang

Saat mengubah tabel dalam beberapa kasus, SSMS menghasilkan skrip yang membuat ulang seluruh tabel dan dalam beberapa kasus yang lebih sederhana (seperti menambahkan atau menghapus kolom) skrip tidak membuat ulang tabel.

Mari kita ambil tabel contoh ini sebagai contoh:

CREATE TABLE [dbo].[Test](
    [ID] [int] NOT NULL,
    [Col1] [nvarchar](50) NOT NULL,
    [Col2] [int] NOT NULL,
 CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

Setiap tabel memiliki LOCK_ESCALATIONpengaturan, yang diatur TABLEsecara default. Mari kita ubah di sini:

ALTER TABLE dbo.Test SET (LOCK_ESCALATION = DISABLE)

Sekarang, jika saya mencoba mengubah Col1tipe di desainer tabel SSMS, SSMS menghasilkan skrip yang membuat ulang seluruh tabel:

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_Test
    (
    ID int NOT NULL,
    Col1 nvarchar(10) NOT NULL,
    Col2 int NOT NULL
    )  ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_Test SET (LOCK_ESCALATION = DISABLE)
GO
IF EXISTS(SELECT * FROM dbo.Test)
     EXEC('INSERT INTO dbo.Tmp_Test (ID, Col1, Col2)
        SELECT ID, CONVERT(nvarchar(10), Col1), Col2 FROM dbo.Test WITH (HOLDLOCK TABLOCKX)')
GO
DROP TABLE dbo.Test
GO
EXECUTE sp_rename N'dbo.Tmp_Test', N'Test', 'OBJECT' 
GO
ALTER TABLE dbo.Test ADD CONSTRAINT
    PK_Test PRIMARY KEY CLUSTERED 
    (
    ID
    ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO
COMMIT

Anda dapat melihat di atas yang ditetapkan LOCK_ESCALATIONuntuk tabel yang baru dibuat. SSMS melakukannya untuk mempertahankan pengaturan tabel saat ini. SSMS membuat baris ini, meskipun nilai setelan saat ini adalah nilai default TABLE. Hanya untuk aman dan eksplisit dan mencegah kemungkinan masalah di masa depan jika di masa depan default ini berubah, saya rasa. Ini masuk akal.

Dalam contoh ini sangat diperlukan untuk menghasilkan SET LOCK_ESCALATIONpernyataan, karena tabel dibuat baru dan pengaturannya harus dipertahankan.

Jika saya mencoba membuat perubahan sederhana ke tabel menggunakan desainer tabel SSMS, seperti menambahkan kolom baru, maka SSMS menghasilkan skrip yang tidak membuat ulang tabel:

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.Test ADD
    NewCol nchar(10) NULL
GO
ALTER TABLE dbo.Test SET (LOCK_ESCALATION = DISABLE)
GO
COMMIT

Seperti yang Anda lihat, itu masih menambahkan ALTER TABLE SET LOCK_ESCALATIONpernyataan, meskipun dalam hal ini tidak diperlukan sama sekali. Yang pertama ALTER TABLE ... ADDtidak mengubah pengaturan saat ini. Saya kira, pengembang SSMS memutuskan bahwa tidak sepadan dengan upaya untuk mencoba menentukan dalam kasus apa ALTER TABLE SET LOCK_ESCALATIONpernyataan ini berlebihan dan selalu menghasilkannya, hanya untuk amannya. Tidak ada salahnya menambahkan pernyataan ini setiap saat.

Sekali lagi, LOCK_ESCALATIONpengaturan seluruh tabel tidak relevan sementara skema tabel berubah melalui ALTER TABLEpernyataan. LOCK_ESCALATIONpengaturan hanya mempengaruhi perilaku penguncian pernyataan DML, seperti UPDATE.

Akhirnya, kutipan dari ALTER TABLE, tekankan milik saya:

Perubahan yang ditentukan dalam ALTER TABLE segera diimplementasikan. Jika perubahan memerlukan modifikasi baris dalam tabel, ALTER TABLE memperbarui baris. ALTER TABLE memperoleh kunci modifikasi skema (SCH-M) pada tabel untuk memastikan tidak ada koneksi lain yang mereferensikan bahkan metadata untuk tabel selama perubahan., kecuali operasi indeks online yang memerlukan kunci SCH-M yang sangat pendek di bagian akhir. Dalam operasi ALTER TABLE… SWITCH, kunci diperoleh pada tabel sumber dan target. Modifikasi yang dilakukan pada tabel dicatat dan dapat dipulihkan sepenuhnya. Perubahan yang memengaruhi semua baris dalam tabel yang sangat besar, seperti menghapus kolom atau, pada beberapa edisi SQL Server, menambahkan kolom NOT NULL dengan nilai default, dapat memakan waktu lama untuk menyelesaikan dan menghasilkan banyak catatan log. Pernyataan ALTER TABLE ini harus dijalankan dengan hati-hati seperti pernyataan INSERT, UPDATE, atau DELETE yang memengaruhi banyak baris.

Vladimir Baranov
sumber