Hiçbir veride değişiklik olmadığı GÜNCELLEME performansı


31

Bir varsa UPDATE(veri güncellenmiş durumda zaten olduğu için) fiilen herhangi bir veri değişmez deyimi. WHEREGüncelleştirmeyi önlemek için maddeye bir kontrol koymanın herhangi bir performans avantajı var mı ?

Örneğin, aşağıdakilerde UPDATE 1 ve UPDATE 2 arasında yürütme hızında herhangi bir fark olur:

CREATE TABLE MyTable (ID int PRIMARY KEY, Value int);
INSERT INTO MyTable (ID, Value)
VALUES
    (1, 1),
    (2, 2),
    (3, 3);

-- UPDATE 1
UPDATE MyTable
SET
    Value = 2
WHERE
    ID = 2
    AND Value <> 2;
SELECT @@ROWCOUNT;

-- UPDATE 2
UPDATE MyTable
SET
    Value = 2
WHERE
    ID = 2;
SELECT @@ROWCOUNT;

DROP TABLE MyTable;

Sormamın nedeni, değişmeyen satırı eklemek için satır sayımına ihtiyacım olduğudur, böylece kimlik yoksa bir ek yapılıp yapılmayacağını biliyorum. Gibi UPDATE 2 formunu kullandım. UPDATE 1 formunu kullanmanın bir performans avantajı varsa, bir şekilde ihtiyacım olan satır sayısını elde etmek mümkün mü?


Bkz sqlperformance.com/2012/10/t-sql-queries/conditional-updates (hayır değerler değişebilir nerede davayı profiline değil gerçi).
Aaron Bertrand

Yanıtlar:


24

Aslında herhangi bir veriyi değiştirmeyen bir UPDATE deyim varsa (veriler zaten güncellenmiş durumda olduğundan), güncelleştirmeyi engellemek için yan tümce maddesini kontrol etmenin herhangi bir performans yararı var mı?

GÜNCELLEME 1 nedeniyle hafif bir performans farkı olduğu için kesinlikle olabilir :

  • aslında hiçbir satırı güncellemiyor (dolayısıyla diske yazacak hiçbir şey yok, hatta minimum günlük etkinliği bile yok) ve
  • Gerçek güncellemeyi yapmak için gerekenden daha az kısıtlayıcı kilitler almak (bu nedenle eşzamanlılık için daha iyidir) ( Lütfen sona doğru Güncelleme bölümüne bakın )

Ancak, sisteminizde sizin tarafınızdan şema, veri ve sistem yükü ile ne kadar farkın ölçülmesi gerekir. Güncelleme yapılmayan bir UPDATE'in ne kadar etkili olduğunu belirleyen çeşitli faktörler vardır:

  • Güncellenen masada çekişme miktarı
  • güncellenen satır sayısı
  • güncellenen masadaki GÜNCELLEME Tetikleyicileri varsa (Soru ile ilgili bir yorumda Mark tarafından belirtildiği gibi). Yürütürseniz UPDATE TableName SET Field1 = Field1, daha sonra bir Güncelleme Tetik ateş edeceği ve (ya kullanarak kontrol ederseniz alan güncellendi belirtmek UPDATE () veya COLUMNS_UPDATED işlevlerini) ve her iki alan olduğunu INSERTEDve DELETEDtablolar aynı değerdir.

Ayrıca, aşağıdaki özet bölüm Paul White'ın Güncelleme Yapılmayan Güncellemelerin Etkisi ( @spaghettidba tarafından yanıtına yaptığı bir yorumda belirtildiği gibi) adlı makalesinde bulunmaktadır:

SQL Server, kalıcı bir veritabanında herhangi bir değişikliğe neden olmayacak bir UPDATE işlemini işlerken gereksiz günlük kaydı veya sayfa temizlemesini önlemek için çeşitli optimizasyonlar içerir.

  • Kümelenmiş bir tablodaki güncelleme olmayan güncellemeler, genellikle, küme anahtarını oluşturan bir sütun (güncelleme işlemi) etkilenmiyorsa, fazladan günlük kaydı yapmaktan ve sayfa temizlemekten kaçınır.
  • Küme tuşunun herhangi bir kısmı aynı değere 'güncellenmiş' ise, işlem sanki veriler değiştirilmiş gibi kaydedilir ve etkilenen sayfalar arabellek havuzunda kirli olarak işaretlenir. Bu, GÜNCELLEME'nin bir silme-sonra-silme işlemine dönüştürülmesinin bir sonucudur.
  • Öbek tabloları, fazladan bir günlüğe kaydetmeye veya sayfa temizlemesine neden olacak bir küme anahtarına sahip olmadıklarından, kümelenmiş tablolarla aynı şekilde davranır. Bu, kümelenmemiş bir birincil anahtarın öbek üzerinde olduğu durumlarda bile geçerlidir. Bir yığında güncellenmeyen güncellemeler, bu nedenle genellikle fazladan kayıt ve yıkamadan kaçınır (ancak aşağıya bakınız).
  • Hem yığınlar hem de kümelenmiş tablolar, 8000 bayttan fazla veri içeren bir LOB sütununun 'SET column_name = column_name' dışındaki herhangi bir sözdizimi kullanılarak aynı değere güncellendiği herhangi bir satır için fazladan günlüğe kaydetme ve temizleme işlemlerine maruz kalır.
  • Basitçe, bir veritabanında herhangi bir tür satır versiyonlama yalıtım seviyesinin etkinleştirilmesi her zaman ekstra günlüklemeye ve yıkamaya neden olur. Bu, güncelleme işlemi için geçerli olan yalıtım seviyesinden bağımsız olarak gerçekleşir.

Lütfen aklınızda bulundurun (özellikle Paul'ün makalesinin tamamını görmek için bağlantıyı takip etmezseniz), aşağıdaki iki madde:

  1. Güncelleme yapılmayan güncellemeler hala bir işlemin başladığını ve bittiğini gösteren bazı günlük faaliyetlerine sahiptir. Sadece hiçbir veri değişikliği gerçekleşmez (bu hala iyi bir tasarruftur).

  2. Yukarıda belirttiğim gibi, sisteminizi test etmeniz gerekiyor. Paul'ün kullandığı araştırma sorgularını kullanın ve aynı sonuçları alıp almadığınızı görün. Sistemimde, makalede gösterilenden biraz farklı sonuçlar görüyorum. Hala yazdırılacak kirli sayfalar yok, ancak biraz daha fazla günlük etkinliği.


... Değişmeyen satırı eklemek için satır sayımına ihtiyacım var, böylece kimlik yoksa ek ekleme yapılıp yapılmayacağını biliyorum. ... bir şekilde ihtiyacım olan satır sayısını almak mümkün mü?

Basitçe, sadece tek bir satır ile ilgileniyorsanız, aşağıdakileri yapabilirsiniz:

UPDATE MyTable
SET    Value = 2
WHERE  ID = 2
AND Value <> 2;

IF (@@ROWCOUNT = 0)
BEGIN
  IF (NOT EXISTS(
                 SELECT *
                 FROM   MyTable
                 WHERE  ID = 2 -- or Value = 2 depending on the scenario
                )
     )
  BEGIN
     INSERT INTO MyTable (ID, Value) -- or leave out ID if it is an IDENTITY
     VALUES (2, 2);
  END;
END;

Birden fazla satır için, OUTPUTmaddeyi kullanarak bu kararı vermek için gereken bilgileri alabilirsiniz . Tam olarak hangi satırların güncellendiğini yakalayarak, mevcut satırları güncellememek yerine var olan güncellemelere ihtiyaç duymamak yerine var olmayan güncelleme satırları değil arasındaki farkı öğrenmek için öğeleri daraltabilirsiniz.

Temel uygulamayı aşağıdaki cevapta gösteriyorum:

Xml parametresini kullanarak birden fazla veriyi yükseltirken Birleştir sorgusunu kullanmaktan nasıl kaçınılır?

Bu cevapta gösterilen yöntem henüz var olan satırları filtrelemez, ancak güncellenmesi gerekmez. Bu kısım eklenebilir, ancak önce birleştiğiniz veri kümenizi tam olarak nereden aldığınızı göstermeniz gerekir MyTable. Geçici bir masadan mı geliyorlar? Tablo değerli bir parametre (TVP)?


GÜNCELLEME 1:

Sonunda bazı testler yapabildim ve işte işlem günlüğü ve kilitleme ile ilgili bulduklarım. İlk olarak, tablonun şeması:

CREATE TABLE [dbo].[Test]
(
  [ID] [int] NOT NULL CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED,
  [StringField] [varchar](500) NULL
);

Ardından, alanı zaten sahip olduğu değerle güncelleyen test:

UPDATE rt
SET    rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM   dbo.Test rt
WHERE  rt.ID = 4082117

Sonuçlar:

-- Transaction Log (2 entries):
Operation
----------------------------
LOP_BEGIN_XACT
LOP_COMMIT_XACT


-- SQL Profiler (3 Lock:Acquired events):
Mode            Type
--------------------------------------
8 - IX          5 - OBJECT
8 - IX          6 - PAGE
5 - X           7 - KEY

Son olarak, değer değişmemesi nedeniyle güncellemeyi filtreleyen test:

UPDATE rt
SET    rt.StringField = '04CF508B-B78E-4264-B9EE-E87DC4AD237A'
FROM   dbo.Test rt
WHERE  rt.ID = 4082117
AND    rt.StringField <> '04CF508B-B78E-4264-B9EE-E87DC4AD237A';

Sonuçlar:

-- Transaction Log (0 entries):
Operation
----------------------------


-- SQL Profiler (3 Lock:Acquired events):
Mode            Type
--------------------------------------
8 - IX          5 - OBJECT
7 - IU          6 - PAGE
4 - U           7 - KEY

Gördüğünüz gibi, İşlemin başlangıcını ve bitişini gösteren iki girişin aksine, satırı filtreleyen İşlem Günlüğü'ne hiçbir şey yazılmaz. Ve bu iki girişin neredeyse hiçbir şey olmadığı doğru olsa da, hala bir şeydir.

Ayrıca, PAGE ve KEY kaynaklarının kilitlenmesi değişmemiş satırları filtrelerken daha az kısıtlayıcıdır. Başka hiçbir işlem bu tabloyla etkileşime girmiyorsa, o zaman sorun olmaz (ama bu gerçekten ne kadar muhtemeldir?). Bağlantılı bloglardan herhangi birinde (ve hatta testlerimde) gösterilen testlerin, hiçbir zaman testlerin bir parçası olmadığı için masada hiçbir çekişme olmadığını varsaydığını unutmayın. Güncelleme yapılmayan güncellemelerin o kadar hafif olduğunu söyleyerek, filtrelemeyi yapmak için para ödemeyin, testin bir vakumda az ya da çok yapıldığı için bir miktar tuzla alınması gerekir. Ancak Üretim'de bu tablo büyük olasılıkla yalıtılmış değildir. Tabii ki, küçük kütük kaydı ve daha kısıtlayıcı kilitlerin daha az verimlilikle sonuçlanmaması çok iyi olabilir. Yani bu soruyu cevaplamak için en güvenilir bilgi kaynağı? SQL Server. özellikle:sizin SQL Server. Sisteminiz için hangi yöntemin daha iyi olduğunu size gösterecektir :-).


GÜNCELLEME 2:

Yeni değerin mevcut değerle aynı olduğu işlemler (yani güncelleme yok), yeni değerin farklı olduğu ve güncellemenin gerekli olduğu işlemleri sayıyorsa, aşağıdaki örnek daha iyi olabilir. Masada çok çekişme var. Fikir, SELECTgeçerli değeri elde etmek için önce basit bir işlem yapmaktır . Eğer bir değer alamazsanız, ilgili cevabınız sizde INSERT. Eğer bir değeriniz varsa, basit bir işlem yapabilir IFve UPDATE sadece gerekli olması durumunda düzenleyebilirsiniz .

DECLARE @CurrentValue VARCHAR(500) = NULL,
        @NewValue VARCHAR(500) = '04CF508B-B78E-4264-B9EE-E87DC4AD237A',
        @ID INT = 4082117;

SELECT @CurrentValue = rt.StringField
FROM   dbo.Test rt
WHERE  rt.ID = @ID;

IF (@CurrentValue IS NULL) -- if NULL is valid, use @@ROWCOUNT = 0
BEGIN
  -- row does not exist
  INSERT INTO dbo.Test (ID, StringField)
  VALUES (@ID, @NewValue);
END;
ELSE
BEGIN
  -- row exists, so check value to see if it is different
  IF (@CurrentValue <> @NewValue)
  BEGIN
    -- value is different, so do the update
    UPDATE rt
    SET    rt.StringField = @NewValue
    FROM   dbo.Test rt
    WHERE  rt.ID = @ID;
  END;
END;

Sonuçlar:

-- Transaction Log (0 entries):
Operation
----------------------------


-- SQL Profiler (2 Lock:Acquired events):
Mode            Type
--------------------------------------
6 - IS          5 - OBJECT
6 - IS          6 - PAGE

Bu yüzden, 3 yerine yalnızca 2 kilit alınmış ve bu kilitlerin her ikisi de Amaçlı Olarak Belirlenmiş veya Amaçlı Güncelleme ( Kilit Uyumluluğu ) değil Amaç Paylaşımlı . Elde edilen her bir kilidin de serbest bırakılacağını unutmayın; her bir kilit gerçekten 2 işlemdir, bu nedenle bu yeni yöntem, önerilen yöntemdeki 6 işlem yerine toplam 4 işlemdir. Bu işlemin her 15 ms'de bir (yaklaşık olarak OP tarafından belirtildiği gibi) bir saniyede 66 kez çalıştığını düşünün. Bu nedenle, orijinal teklif saniyede 396 kilitleme / kilit açma işlemi tutarken, bu yeni yöntem daha hafif kilitlerin bile saniye başına yalnızca 264 kilitleme / kilit açma işlemi tutarındadır. Bu, mükemmel performansın garantisi değil, kesinlikle test etmeye değerdir :-).


14

Biraz uzaklaştırın ve daha büyük resmi düşünün. Gerçek dünyada, güncelleme ifadeniz gerçekten şuna benzeyecek mi:

UPDATE MyTable
  SET Value = 2
WHERE
     ID = 2
     AND Value <> 2;

Yoksa şuna daha çok benzeyecek mi:

UPDATE Customers
  SET AddressLine1 = '123 Main St',
      AddressLine2 = 'Apt 24',
      City = 'Chicago',
      State = 'IL',
      (and a couple dozen more fields)
WHERE
     ID = 2
     AND (AddressLine1 <> '123 Main St'
     OR AddressLine2 <> 'Apt 24'
     OR City <> 'Chicago'
     OR State <> 'IL'
      (and a couple dozen more fields))

Çünkü gerçek dünyada masaların çok fazla sütunu var. Bu, dinamik dizeler oluşturmak için çok sayıda karmaşık dinamik uygulama mantığı oluşturmak zorunda kalacağınız anlamına gelir, VEYA her alanın öncesi ve sonrası içeriğini her zaman belirtmeniz gerekir.

Bu güncelleme bildirimlerini her tablo için dinamik olarak oluşturursanız, yalnızca güncellenmekte olan alanlardan geçerek, birkaç yıl öncesinden NHibernate parametre boyutları sorununa benzer bir şekilde bir önbellek kirliliği sorunuyla hızlı bir şekilde karşılaşabilirsiniz . Daha da kötüsü, güncelleme deyimlerini SQL Server'da (saklı yordamlarda olduğu gibi) oluşturursanız, o zaman değerli CPU çevrimlerini yazacaksınız çünkü SQL Server ölçekleri bir araya getirerek dizeleri birleştirmede çok etkili değildir.

Bu karmaşıklıklar nedeniyle, güncellemeleri yaparken bu tür satır satır, alan bazında karşılaştırma yapmak genellikle mantıklı değildir. Bunun yerine set tabanlı işlemleri düşünün.


1
Benim gerçek dünya örneğim bu kadar basit ama çok deniyor. Tahminim en yoğun zamanlarda her 15ms'de birdir. SQL Server'ın gerekmediğinde diske yazmayacak kadar cleaver olup olmadığını merak ediyordum.
Martin Brown

3

Yalnızca satır sayısı büyük olduğunda güncellenmesi gerekmeyen satırları atlarken bir performans kazancı görebilirsiniz (daha az günlük kaydı, daha az kirli sayfalar diske yazılabilir).

Tek satır güncellemeleriyle ilgili olarak durumunuzdaki gibi, performans farkı tamamen göz ardı edilebilir. Her durumda satırları güncellemek sizin için kolaylaştırıyorsa, yapın.

Konuyla ilgili daha fazla bilgi için bkz . Paul White'dan Güncelleme Yapmama



1

Tüm alanların değerlerini kontrol etmek yerine, ilgilendiğiniz sütunları kullanarak bir karma değer elde edemezsiniz, sonra bunu tablodaki satıra karşı saklanan karma ile karşılaştıramaz mısınız?

IF EXISTS (Select 1 from Table where ID =@ID AND HashValue=Sha256(column1+column2))
GOTO EXIT
ELSE
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.