İsteği doğru anlarsam, amaç satır sıralarını silerken, aynı zamanda DML işlemleri tablodaki satırlarda gerçekleşir. Amaç bir toplu işlemi silmek; Bununla birlikte, söz konusu parti tarafından tanımlanan aralık içinde yer alan herhangi bir satır kilitliyse, o grubu atlamalı ve bir sonraki gruba geçmeliyiz. Daha önce silinmemiş herhangi bir gruba geri dönmeli ve orijinal silme mantığımızı yeniden denemeliyiz. Tüm gerekli satır grupları silinene kadar bu döngüyü tekrarlamalıyız.
Belirtildiği gibi, engellenen satırları içerebilecek geçmiş aralıkları atlamak için bir READPAST ipucu ve READ COMMITTED (varsayılan) yalıtım seviyesinin kullanılması makul olacaktır. Bir adım daha ileri gidip SERIALIZABLE izolasyon seviyesi ve nibbling silmelerini kullanmanızı öneririm.
SQL Server, seri hale getirilebilir işlem yalıtım düzeyini kullanırken Transact-SQL deyimi tarafından okunan bir kayıt kümesinde örtülü olarak bulunan bir dizi satırı korumak için Anahtar Aralığı kilitlerini kullanır ... daha fazlasını burada bulabilirsiniz:
https://technet.microsoft.com /en-US/library/ms191272(v=SQL.105).aspx
Nibbling silme ile hedefimiz, bir dizi satırı izole etmek ve bunları silerken bu satırlarda hiçbir değişiklik olmayacağından emin olmaktır, yani, hayalet okumalar veya eklemeler istemiyoruz. Serileştirilebilir izolasyon seviyesi bu sorunu çözmeyi amaçlamaktadır.
Çözümümü göstermeden önce, veritabanınızın varsayılan yalıtım düzeyini SERIALIZABLE olarak değiştirmeyi önermediğimi ya da çözümümün en iyisi olmasını önermiyorum. Sadece onu sunmak ve buradan nereye gidebileceğimizi görmek istiyorum.
Birkaç ev tutma notu:
- Kullandığım SQL Server sürümü Microsoft SQL Server 2012 - 11.0.5343.0 (X64)
- Test veritabanım TAM kurtarma modelini kullanıyor
Denememe başlamak için, bir test veritabanı ve örnek bir tablo oluşturacağım ve tabloyu 2.000.000 satırla dolduracağım.
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
Bu noktada, SERİLEŞTİRilebilir yalıtım seviyesinin kilitleme mekanizmalarının üzerinde etkili olabileceği bir veya daha fazla endekse ihtiyacımız olacak.
-- 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
Şimdi 2.000.000 satırımızın oluşturulduğunu kontrol edelim.
SELECT
COUNT(*)
FROM
tbl;
Böylece, veritabanımıza, tablomuza, indekslere ve satırlara sahibiz. Öyleyse, nibbling silmeleri için deney hazırlayalım. İlk önce, tipik bir nibbling silme mekanizması oluşturmanın en iyi yoluna karar vermeliyiz.
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;
Gördüğünüz gibi, açık işlemi while döngüsünün içine yerleştirdim. Günlük sifonlarını sınırlamak istiyorsanız, o zaman döngünün dışına yerleştirmekten çekinmeyin. Ayrıca, TAM kurtarma modelinde olduğumuz için, işlem günlüğünüzün aşırı derecede büyümesinin önlenebilmesini sağlamak için nibbling silme işlemlerinizi gerçekleştirirken işlem günlüğü yedeklemelerini daha sık oluşturmak isteyebilirsiniz.
Yani, bu kurulum ile birkaç hedefim var. İlk olarak, anahtar aralık kilitlerimi istiyorum; bu yüzden partileri mümkün olduğunca küçük tutmaya çalışıyorum. Ayrıca "devasa" masamdaki eşzamanlılığı olumsuz etkilemek de istemiyorum; bu yüzden kilitlerimi alıp olabildiğince hızlı bırakmak istiyorum. Bu yüzden parti büyüklüğünüzü küçük kılmanızı öneririm.
Şimdi, bu silme rutininin çok kısa bir örneğini işlem yapmak istiyorum. SSMS'de yeni bir pencere açmalı ve tablomuzdan bir satır silmeliyiz. Bunu, varsayılan READ COMMITTED izolasyon seviyesini kullanarak gizli bir işlem içinde yapacağım.
DELETE FROM
dbo.tbl
WHERE
c1 = 20005;
Bu satır gerçekten silindi mi?
SELECT
c1
FROM
dbo.tbl
WHERE
c1 BETWEEN 20000 AND 20010;
Evet silindi.
Şimdi, kilitlerimizi görmek için, SSMS içinde yeni bir pencere açalım ve bir veya iki kod kodu ekleyelim. Burada bulunan Adam Mechanic sp_whoisactive kullanıyorum: 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;
Şimdi başlamaya hazırız. Yeni bir SSMS penceresinde, sildiğimiz bir satırı tekrar yerleştirmeye çalışacak açık bir işlem başlayalım. Aynı zamanda nibbling silme işlemimizi de başlatacağız.
İnsert kodu:
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;
Her iki işlemi de eklenti ile başlayıp silme işlemlerimizle başlatalım. Anahtar aralıklı kilitleri ve özel kilitleri görebiliriz.
Ek bu kilitleri oluşturdu:
Nibbling delete / select bu kilitleri tutuyor:
Ekimiz, silinmemizi beklendiği gibi engelliyor:
Şimdi ekleme işlemini yapalım ve ne olduğunu görelim.
Beklenildiği gibi tüm işlemler tamamlandı. Şimdi, ekin bir hayalet olup olmadığını veya silme işleminin de kaldırılıp kaldırılmadığını kontrol etmeliyiz.
SELECT
c1
FROM
dbo.tbl
WHERE
c1 BETWEEN 20000 AND 20015;
Aslında, ek silindi; bu yüzden, hiçbir hayalet eke izin verilmedi.
Dolayısıyla, sonuç olarak, bu alıştırmanın asıl amacının her satır, sayfa veya tablo düzeyinde kilitlemeyi denemek ve izlememek ve bir toplu iş öğesinin kilitli olup olmadığını belirlemeye çalışmak ve bu nedenle silme işlemimizin yapılmasını gerektireceğini düşünüyorum. Bekleyin. Bu, sorgulayıcıların amacı olabilirdi; Ancak, bu görev herculean ve imkansız değilse de temelde pratik değildir. Asıl amaç, parti yelpazemizi kendi kilitlerimizle izole ettikten ve daha sonra parti silme işleminden önce bir fenomenin ortaya çıkmamasını sağlamaktır. SERİLEŞTİRİLEN izolasyon seviyesi bu hedefe ulaşır. Kilit nokta, küçük uçlarınızı küçük tutmak, işlem günlüğünüzü kontrol altında tutmak ve istenmeyen olayları ortadan kaldırmaktır.
Hız istiyorsanız, o zaman bölümlendirilemeyen devasa derin masalar oluşturmayın ve bu nedenle en hızlı sonuç için bölüm değiştirme kullanamazsınız. Hızın anahtarı bölümleme ve paralelliktir; ıstırabın anahtarı çirkinlikler ve canlı kilitlemedir.
Lütfen ne düşündüğünü bilmeme izin ver.
SERİLEŞTİRİLEN yalıtım seviyesinin çalışmadaki bazı örneklerini oluşturdum. Aşağıdaki linklerde mevcut olmalıdırlar.
İşlemi Sil
İşlem ekle
Eşitlik Operasyonları - Gelecek Anahtar Değerlerde Anahtar Aralığı Kilitleri
Eşitlik Operasyonları - Singleton Mevcut Verilerin Alınması
Eşitlik Operasyonları - Singleton, Var Olmayan Verileri Getiriyor
Eşitsizlik İşlemleri - Menzil Anahtar Anahtar Kilitleri ve Sonraki Anahtar Değerler