Jika saya memahami permintaan dengan benar, tujuannya adalah untuk menghapus kumpulan baris, sementara pada saat yang sama, operasi DML terjadi pada baris di seluruh tabel. Tujuannya adalah untuk menghapus kumpulan; namun, jika ada baris mendasar yang terkandung dalam kisaran yang ditentukan oleh kumpulan tersebut dikunci, maka kita harus melewati kumpulan itu dan pindah ke kumpulan berikutnya. Kami kemudian harus kembali ke kumpulan mana pun yang sebelumnya tidak dihapus dan coba lagi logika hapus asli kami. Kita harus mengulangi siklus ini sampai semua kumpulan baris yang diperlukan dihapus.
Seperti yang telah disebutkan, masuk akal untuk menggunakan petunjuk READPAST dan tingkat isolasi BACA (default), untuk melewati rentang masa lalu yang mungkin berisi baris yang diblokir. Saya akan melangkah lebih jauh dan merekomendasikan menggunakan tingkat isolasi SERIALIZABLE dan menggigit menghapus.
SQL Server menggunakan kunci Rentang Kunci untuk melindungi serangkaian baris yang secara implisit termasuk dalam kumpulan catatan yang sedang dibaca oleh pernyataan Transact-SQL saat menggunakan tingkat isolasi transaksi yang dapat disesuaikan ... temukan lebih lanjut di sini:
https://technet.microsoft.com /en-US/library/ms191272(v=SQL.105).aspx
Dengan menggigit menghapus, tujuan kami adalah untuk mengisolasi berbagai baris dan memastikan bahwa tidak ada perubahan yang terjadi pada baris tersebut saat kami menghapusnya, artinya, kami tidak ingin pembacaan atau penyisipan phantom. Tingkat isolasi serializable dimaksudkan untuk menyelesaikan masalah ini.
Sebelum saya mendemonstrasikan solusi saya, saya ingin menambahkan bahwa saya juga tidak merekomendasikan untuk beralih tingkat isolasi standar database Anda ke SERIALIZABLE juga saya merekomendasikan solusi saya adalah yang terbaik. Saya hanya ingin menyampaikannya dan melihat ke mana kita bisa pergi dari sini.
Beberapa catatan perawatan rumah:
- Versi SQL Server yang saya gunakan adalah Microsoft SQL Server 2012 - 11.0.5343.0 (X64)
- Basis data pengujian saya menggunakan model pemulihan LENGKAP
Untuk memulai percobaan saya, saya akan membuat basis data pengujian, tabel sampel, dan saya akan mengisi tabel dengan 2.000.000 baris.
USE [master];
GO
SET NOCOUNT ON;
IF DATABASEPROPERTYEX (N'test', N'Version') > 0
BEGIN
ALTER DATABASE [test] SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
DROP DATABASE [test];
END
GO
-- Create the test database
CREATE DATABASE [test];
GO
-- Set the recovery model to FULL
ALTER DATABASE [test] SET RECOVERY FULL;
-- Create a FULL database backup
-- in order to ensure we are in fact using
-- the FULL recovery model
-- I pipe it to dev null for simplicity
BACKUP DATABASE [test]
TO DISK = N'nul';
GO
USE [test];
GO
-- Create our table
IF OBJECT_ID('dbo.tbl','U') IS NOT NULL
BEGIN
DROP TABLE dbo.tbl;
END;
CREATE TABLE dbo.tbl
(
c1 BIGINT IDENTITY (1,1) NOT NULL
, c2 INT NOT NULL
) ON [PRIMARY];
GO
-- Insert 2,000,000 rows
INSERT INTO dbo.tbl
SELECT TOP 2000
number
FROM
master..spt_values
ORDER BY
number
GO 1000
Pada titik ini, kita akan memerlukan satu atau lebih indeks di mana mekanisme penguncian tingkat isolasi SERIALIZABLE dapat bertindak.
-- Add a clustered index
CREATE UNIQUE CLUSTERED INDEX CIX_tbl_c1
ON dbo.tbl (c1);
GO
-- Add a non-clustered index
CREATE NONCLUSTERED INDEX IX_tbl_c2
ON dbo.tbl (c2);
GO
Sekarang, mari kita periksa untuk melihat apakah 2.000.000 baris kita telah dibuat
SELECT
COUNT(*)
FROM
tbl;
Jadi, kami memiliki basis data, tabel, indeks, dan baris kami. Jadi, mari kita siapkan percobaan untuk menghapus gigitan. Pertama, kita harus memutuskan cara terbaik untuk membuat mekanisme penghapusan yang biasa-biasa saja.
DECLARE
@BatchSize INT = 100
, @LowestValue BIGINT = 20000
, @HighestValue BIGINT = 20010
, @DeletedRowsCount BIGINT = 0
, @RowCount BIGINT = 1;
SET NOCOUNT ON;
GO
WHILE @DeletedRowsCount < ( @HighestValue - @LowestValue )
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION
DELETE
FROM
dbo.tbl
WHERE
c1 IN (
SELECT TOP (@BatchSize)
c1
FROM
dbo.tbl
WHERE
c1 BETWEEN @LowestValue AND @HighestValue
ORDER BY
c1
);
SET @RowCount = ROWCOUNT_BIG();
COMMIT TRANSACTION;
SET @DeletedRowsCount += @RowCount;
WAITFOR DELAY '000:00:00.025';
CHECKPOINT;
END;
Seperti yang Anda lihat, saya menempatkan transaksi eksplisit di dalam loop sementara. Jika Anda ingin membatasi flush log, silakan letakkan di luar loop. Selain itu, karena kita berada dalam model pemulihan LENGKAP, Anda mungkin ingin membuat cadangan log transaksi lebih sering saat menjalankan operasi penghapusan gigitan Anda, untuk memastikan bahwa log transaksi Anda dapat dicegah agar tidak bertambah besar.
Jadi, saya punya beberapa tujuan dengan pengaturan ini. Pertama, saya ingin kunci rentang kunci saya; jadi, saya mencoba untuk menjaga batch sekecil mungkin. Saya juga tidak ingin berdampak negatif pada konkurensi di atas meja "raksasa" saya; jadi, saya ingin mengambil kunci saya dan meninggalkannya secepat mungkin. Jadi, saya sarankan Anda membuat ukuran batch Anda kecil.
Sekarang, saya ingin memberikan contoh singkat tentang tindakan penghapusan rutin ini. Kami harus membuka jendela baru dalam SSMS dan menghapus satu baris dari tabel kami. Saya akan melakukan ini dalam transaksi implisit menggunakan tingkat isolasi BACA KOMITMEN standar.
DELETE FROM
dbo.tbl
WHERE
c1 = 20005;
Apakah baris ini benar-benar dihapus?
SELECT
c1
FROM
dbo.tbl
WHERE
c1 BETWEEN 20000 AND 20010;
Ya, sudah dihapus.
Sekarang, untuk melihat kunci kami, mari kita buka jendela baru dalam SSMS dan tambahkan satu atau dua potongan kode. Saya menggunakan sp_whoisactive dari Adam Mechanic, yang dapat ditemukan di sini: sp_whoisactive
SELECT
DB_NAME(resource_database_id) AS DatabaseName
, resource_type
, request_mode
FROM
sys.dm_tran_locks
WHERE
DB_NAME(resource_database_id) = 'test'
AND resource_type = 'KEY'
ORDER BY
request_mode;
-- Our insert
sp_lock 55;
-- Our deletions
sp_lock 52;
-- Our active sessions
sp_whoisactive;
Sekarang, kita siap untuk memulai. Di jendela SSMS baru, mari kita mulai transaksi eksplisit yang akan mencoba memasukkan kembali satu baris yang kita hapus. Pada saat yang sama, kami akan mematikan operasi penghapusan gigitan kami.
Kode yang dimasukkan:
BEGIN TRANSACTION
SET IDENTITY_INSERT dbo.tbl ON;
INSERT INTO dbo.tbl
( c1 , c2 )
VALUES
( 20005 , 1 );
SET IDENTITY_INSERT dbo.tbl OFF;
--COMMIT TRANSACTION;
Mari kita mulai kedua operasi yang dimulai dengan sisipan dan diikuti oleh penghapusan kita. Kita dapat melihat kunci rentang kunci dan kunci eksklusif.
Sisipan menghasilkan kunci-kunci ini:
The nibbling delete / select memegang kunci-kunci ini:
Sisipan kami mencekal penghapusan kami seperti yang diharapkan:
Sekarang, mari kita lakukan transaksi insert dan lihat apa yang terjadi.
Dan seperti yang diharapkan, semua transaksi selesai. Sekarang, kita harus memeriksa untuk melihat apakah sisipan itu adalah hantu atau apakah operasi penghapusan juga menghapusnya.
SELECT
c1
FROM
dbo.tbl
WHERE
c1 BETWEEN 20000 AND 20015;
Bahkan, sisipan telah dihapus; jadi, tidak ada sisipan hantu yang diizinkan.
Jadi, sebagai kesimpulan, saya pikir maksud sebenarnya dari latihan ini bukan untuk mencoba dan melacak setiap baris, halaman, atau kunci level-tabel dan mencoba untuk menentukan apakah elemen batch dikunci dan karenanya akan memerlukan operasi penghapusan kami untuk Tunggu. Itu mungkin maksud para penanya; Namun, tugas itu sangat besar dan pada dasarnya tidak praktis jika tidak mustahil. Tujuan sebenarnya adalah untuk memastikan bahwa tidak ada fenomena yang tidak diinginkan muncul setelah kami mengisolasi kisaran batch kami dengan kunci kami sendiri dan kemudian mendahului untuk menghapus batch. Level isolasi SERIALIZABLE mencapai tujuan ini. Kuncinya adalah untuk menjaga camilan Anda tetap kecil, log transaksi Anda terkendali, dan menghilangkan fenomena yang tidak diinginkan.
Jika Anda menginginkan kecepatan, maka jangan buat tabel raksasa yang tidak dapat dipartisi dan oleh karena itu tidak dapat menggunakan pengalihan partisi untuk hasil tercepat. Kunci kecepatan adalah partisi dan paralelisme; kunci penderitaan adalah mengunyah dan mengunci hidup.
Tolong beritahu saya bagaimana menurut anda.
Saya membuat beberapa contoh lebih lanjut dari tingkat isolasi SERIALIZABLE dalam aksi. Mereka harus tersedia di tautan di bawah ini.
Hapus Operasi
Masukkan Operasi
Operasi Kesetaraan - Kunci-Rentang Kunci pada Nilai-nilai Kunci Berikutnya
Operasi Kesetaraan - Pengambilan Singleton dari Data yang Ada
Operasi Kesetaraan - Pengambilan Singleton dari Data Tidak Ada
Operasi Ketimpangan - Kunci-Rentang Kunci pada Rentang dan Nilai-nilai Kunci Berikutnya