Bu while döngüsünde açık işlemlere gerek var mı?


11

SQL Server 2014:

Çok büyük (100 milyon satır) bir masamız var ve üzerinde birkaç alanı güncellememiz gerekiyor.

Günlük nakliye, vb. İçin, açıkçası, ısırık boyutunda işlemlerde tutmak istiyoruz.

Aşağıdakilerin bir süre çalışmasına izin verirsek ve ardından sorguyu iptal edersek / sonlandırırsak, şimdiye kadar yapılan işlerin tümü işlenecek mi yoksa herhangi bir zamanda iptal edebilmemiz için açık BEGIN TRANSACTION / END TRANSACTION ifadeleri eklememiz gerekiyor mu?

DECLARE @CHUNK_SIZE int
SET @CHUNK_SIZE = 10000

UPDATE TOP(@CHUNK_SIZE) [huge-table] set deleted = 0, deletedDate = '2000-01-01'
where deleted is null or deletedDate is null

WHILE @@ROWCOUNT > 0
BEGIN
    UPDATE TOP(@CHUNK_SIZE) [huge-table] set deleted = 0, deletedDate = '2000-01-01'
    where deleted is null or deletedDate is null
END

Yanıtlar:


13

Bireysel ifadeler - DML, DDL, vb. - kendi içlerinde işlemlerdir. Yani evet, döngünün her yinelemesinden sonra (teknik olarak: her ifadeden sonra), bu UPDATEifade ne olursa olsun otomatik olarak taahhüt edilmiştir.

Tabii ki, her zaman bir istisna vardır, değil mi? Örtülü İşlemleri SET IMPLICIT_TRANSACTIONS aracılığıyla etkinleştirmek mümkündür , bu durumda ilk UPDATEifade, yapmanız gereken COMMITveya ROLLBACKsonunda yapacağınız bir işlemi başlatır . Bu, çoğu durumda varsayılan olarak KAPALI olan bir oturum düzeyi ayarıdır.

istediğiniz zaman iptal edebilmemiz için açık BEGIN TRANSACTION / END TRANSACTION ifadeleri eklememiz gerekir mi?

Hayır. Ve aslında, süreci durdurmak ve yeniden başlatmak istediğiniz göz önüne alındığında, açık bir işlem eklemek (veya Örtülü İşlemleri etkinleştirmek) kötü bir fikir olacaktır çünkü işlemin durdurulması işlemi gerçekleştirmeden önce yakalayabilir COMMIT. Bu durumda COMMIT(SSMS'deyseniz) el ile vermeniz gerekir veya bunu bir SQL Agent işinden çalıştırıyorsanız, bu fırsatınız olmaz ve artık bir işlemle sonuçlanabilir.


Ayrıca, @CHUNK_SIZEdaha küçük bir sayıya ayarlamak isteyebilirsiniz . Kilit yükseltme genellikle tek bir nesnede elde edilen 5000 kilitte gerçekleşir. Satırların boyutuna bağlı olarak ve Satır kilitleri ile Sayfa kilitleri karşılaştırması yapıyorsa, bu sınırı aşmış olabilirsiniz. Bir satırın boyutu her sayfa için yalnızca 1 veya 2 satır sığacak şekildeyse, Sayfa kilitleri yapsa bile buna her zaman çarpıyor olabilirsiniz.

Tablo bölümlenmişse, tabloyu LOCK_ESCALATION(SQL Server 2008'de tanıtıldı) seçeneğini AUTO, tırmanma sırasında tüm tabloyu değil tüm bölümü kilitleyecek şekilde ayarlama seçeneğiniz vardır . Veya, herhangi bir tablo için aynı seçeneği ayarlayabilirsiniz DISABLE, ancak bu konuda çok dikkatli olmanız gerekir. Bkz alter table Ayrıntılar için.

Kilit Yükseltme ve eşikler hakkında konuşan bazı belgeler: Kilit Yükseltme ("SQL Server 2008 R2 ve daha yüksek sürümler için geçerlidir"). Ve burada kilit yükselme tespit ve düzeltme ile ilgili bir blog yazısı: Microsoft SQL Server'da Kilitleme (Bölüm 12 - Kilit Yükseltme) .


Tam soru ile ilgisi yoktur, ancak sorudaki sorgu ile ilgili olarak, burada yapılabilecek birkaç iyileştirme vardır (veya en azından sadece ona bakmaktan böyle görünüyor):

  1. WHILE (@@ROWCOUNT = @CHUNK_SIZE)Döngünüz için, son yinelemede güncellenen satır sayısı GÜNCELLEME için istenen miktardan azsa , yapmak biraz daha iyidir, o zaman yapacak hiçbir iş kalmaz.

  2. Eğer deletedalan bir olan BITveri türü, daha sonra alıp almamalarına değer belirlemiş değil deletedDateise 2000-01-01? Neden ikisine de ihtiyacınız var?

  3. Bu iki alan yeniyse ve bunları NULLçevrimiçi / engellemeyen bir işlem olabilir ve şimdi "varsayılan" değerlerine güncellemek istiyorsanız , bu gerekli değildi. SQL Server 2012'den (yalnızca Enterprise Edition) başlayarak, NOT NULLDEFAULT kısıtlaması olan sütunlar eklemek , DEFAULT değeri sabit olduğu sürece engelleme olmayan işlemlerdir. Bu nedenle henüz alanları kullanmıyorsanız, NOT NULLDEFAULT kısıtlaması olarak bırakıp yeniden ekleyin .

  4. Bu GÜNCELLEME'yi gerçekleştirirken bu alanları güncelleyen başka bir işlem yoksa, güncelleştirmek istediğiniz kayıtları sıraya koyduysanız ve daha sonra yalnızca bu kuyruğu kapatırsanız daha hızlı olur. Değiştirilmesi gereken seti almak için her seferinde tabloyu yeniden sorgulamanız gerektiğinden geçerli yöntemde bir performans isabeti vardır. Bunun yerine, tabloyu bu iki alanda yalnızca bir kez tarayan ve daha sonra yalnızca çok hedefli UPDATE deyimleri yayınlayan aşağıdakileri yapabilirsiniz. Ayrıca, işlemin herhangi bir zamanda durdurulmasından ve daha sonra başlatılmasından herhangi bir ceza yoktur, çünkü kuyruğun ilk nüfusu güncellenecek kalan kayıtları bulacaktır.

    1. Kümelenmiş dizindeki anahtar alanları içeren geçici bir tablo (#FullSet) oluşturun.
    2. Aynı yapının ikinci bir geçici tablosunu (#CurrentSet) oluşturun.
    3. üzerinden #FullSet'e ekle SELECT TOP(n) KeyField1, KeyField2 FROM [huge-table] where deleted is null or deletedDate is null;

      TOP(n)Nedeniyle tablonun büyüklüğüne orada. Tablodaki 100 Milyon satır ile, özellikle işlemi sık sık durdurmayı ve daha sonra yeniden başlatmayı planlıyorsanız, kuyruk tablosunu tüm anahtar kümesiyle doldurmanıza gerek yoktur. Bu yüzden belki n1 milyona ayarlandı ve tamamlanmasına izin ver. Bunu her zaman 1 milyon (veya belki daha az) kümesini çalıştıran bir SQL Agent işinde zamanlayabilir ve daha sonra bir sonraki planlanan sürenin tekrar toplanmasını bekleyebilirsiniz. Daha sonra, her 20 dakikada bir çalışmayı planlayabilirsiniz, böylece setler arasında zorlanmış bir solunum odası nolacaktır, ancak yine de tüm süreci katılımsız olarak bitirecektir. Daha sonra yapacak bir şey olmadığında işin kendisini silmesini sağlayın :-).

    4. bir döngüde şunları yapın:
      1. Geçerli toplu işi benzer bir şeyle doldurma DELETE TOP (4995) FROM #FullSet OUTPUT Deleted.KeyField INTO #CurrentSet (KeyField);
      2. IF (@@ROWCOUNT = 0) BREAK;
      3. UPDATE gibi bir şey kullanarak yapın: UPDATE ht SET ht.deleted = 0, ht.deletedDate='2000-01-01' FROM [huge-table] ht INNER JOIN #CurrentSet cs ON cs.KeyField = ht.KeyField;
      4. Geçerli seti temizle: TRUNCATE TABLE #CurrentSet;
  5. Bazı durumlarda SELECT, #FullSetgeçici tabloya beslenmesine yardımcı olmak için bir Filtrelenmiş Dizin eklemek yardımcı olur . İşte böyle bir dizin eklemeyle ilgili bazı noktalar:
    1. WHERE koşulu, sorgunuzun WHERE koşuluyla eşleşmelidir; WHERE deleted is null or deletedDate is null
    2. İşlemin başlangıcında, çoğu satır WHERE koşulunuzla eşleşir, bu nedenle bir dizin o kadar da yararlı değildir. Bunu eklemeden önce% 50 civarında bir yere kadar beklemek isteyebilirsiniz. Tabii ki, ne kadar yardımcı olur ve endeksi eklemenin en iyi olduğu zaman birkaç faktörden dolayı değişir, bu yüzden biraz deneme yanılmadır.
    3. Temel veriler oldukça sık değiştiği için, istatistikleri manuel olarak GÜNCELLEMEK ve / veya dizini tekrar güncellemek zorunda kalabilirsiniz
    4. Dizin, yardımcı olurken SELECT, UPDATEbu işlem sırasında güncellenmesi gereken başka bir nesne, dolayısıyla daha fazla G / Ç olduğu için zarar vereceğini unutmayın . Bu, hem Filtrelenmiş Bir Dizini (daha az sayıda satır filtreyle eşleştiği için satırları güncellediğinizde küçülür) hem de dizini eklemek için biraz beklerken (başlangıçta süper yardımcı olmayacaksa, bunun için bir neden yok) ek G / Ç).

GÜNCELLEME: Durumu izlemek ve temiz bir şekilde iptal etmek için bir mekanizma da dahil olmak üzere yukarıda önerilenlerin tam olarak uygulanması için bu soru ile ilgili bir soruya cevabımı bakın: sql server: küçük parçalar halinde büyük tablodaki alanları güncelleme: nasıl alınır ilerleme / durumu?


# 4'teki önerileriniz bazı durumlarda daha hızlı olabilir, ancak bu, eklenecek önemli kod karmaşıklığı gibi görünüyor. Basit başlamayı tercih ederim ve sonra bu ihtiyaçlarınızı karşılamıyorsa alternatifleri düşünün.
Bacon Bits

@BaconBits Basit başlangıç ​​konusunda anlaştı. Adil olmak gerekirse, bu önerilerin tüm senaryolara uygulanması amaçlanmamıştır. Soru çok büyük bir tabloyla (100 milyondan fazla sıra) ilgilenmektir.
Solomon Rutzky
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.