SQL Server tablosundaki değişiklikleri algılama


13

Uygulamamda, SQL Server 2012 üzerinde çalışan bir DB ile, düzenli olarak pahalı bir sorgu yürüten ve sonuçları daha sonra uygulama tarafından sorgulanabilecek bir tabloya yazan bir iş (zamanlanmış görev) var.

İdeal olarak, bu pahalı sorguyu yalnızca sorgu son yürütüldüğünden beri bir şey değiştiğinde çalıştırmak istiyorum. Kaynak tabloları çok büyük olduğundan, tüm aday sütunları veya bunun gibi bir şey için bir sağlama toplamı seçemiyorum.

Aşağıdaki fikirlerim var:

  • Kaynak tablodaki bir şeyi her değiştirdiğimde, son değiştirilen zaman damgasını, "sorgular olmalı" bayrağını veya bunun gibi bir şeyi bir izleme tablosuna açıkça yazın.
  • Aynı şeyi yapmak için bir tetikleyici kullanın.

Ancak, yazmaları açıkça izlemeden bir tablodaki değişiklikleri tespit etmenin hafif bir yolu olup olmadığını gerçekten bilmek istiyorum. Örneğin, ROWVERSIONbir tablonun "akımını" veya bunun gibi bir şeyi alabilir miyim?

Yanıtlar:


14

Hayır, yok. Her türlü 'son güncelleme tarihi' izleme, tüm işlemlerden gelen tüm güncellemeler, 'son güncelleme tarihi' kaydını izleyen bir kaydı güncellemeye çalışacağından ciddi bir performans sorunuyla karşılaşır. Bu etkin yalnızca bir işlem her an tablosunu güncelleyebilir anlamına geleceğini ve tüm diğer işlemler için ilk biri için beklemek zorunda işlemek . Serileştirmeyi tamamlayın. Sadece son güncellemenin ne zaman gerçekleştiğini bilmek için bu performans cezasına katlanmak isteyen yöneticilerin / geliştiricilerin sayısı muhtemelen azdır.

Böylece özel kod ile işlemek için mahsur kalırsınız. Alternatif (günlük kayıtlarından algılama) yalnızca işlemsel çoğaltma için ayrılmış bir ayrıcalık (veya CDC alter-ego) olduğu için tetikleyiciler anlamına gelir . Bir 'son güncelleme tarihi' sütunu üzerinden izlemeye çalışırsanız, yukarıda belirtilen serileştirme sorunuyla karşı karşıya kalacağınızı unutmayın. Güncelleme eşzamanlılığı önemliyse, bir kuyruk mekanizması kullanmanız gerekir (tetikleyici bir INSERT kullanır ve daha sonra bir işlem, eklenen son değerleri 'formüle etmek için toplanan değerleri toplar). Mevcut kimliğe gizlice girmek veya sys.dm_db_index_usage_stats'a bakmak gibi 'akıllı' bir çözümle hile yapmaya çalışmayın . Ayrıca Rails zaman damgalarında olduğu gibi, kayıt başına 'updated_at' sütunu,

'Hafif' bir alternatif var mı? Aslında bir tane var, ama sizin için işe yarayıp yaramayacağını söylemek zor: Sorgu Bildirimleri . Sorgu Bildirimi tam olarak bunu yapar, herhangi bir veri değişikliği varsa ve sorgunuzu yenilemeniz gerekiyorsa bir bildirim oluşturur . En Devs SqlDependency olarak .Net oluşum da sadece tanıdık olmasına rağmen, Sorgu Bildirim olabilir uzun ömürlü olarak kullanılabilir, veri değişimini algılamak için bir mekanizma devam etti. Gerçek değişiklik izleme ile karşılaştırıldığında, gerçekten hafif olacak ve anlambilimi ihtiyaçlarınıza daha yakın olacaktır (bir şey, herhangi bir şey değişti, bu yüzden sorguyu yeniden çalıştırmanız gerekir).

Ama sonunda, senin yerinde, varsayımlarımı gerçekten tekrar gözden geçirir ve çizim tahtasına geri dönerdim. Belki de farklı bir sunucuda raporlama veritabanı oluşturmak için günlük gönderim veya çoğaltmayı kullanabilirsiniz. Satırlar arasında okuduğum şey, uygun bir ETL boru hattına ve bir analitik veri deposuna ihtiyacınız olması ...


Öyleyse, eğer sağladığı bilgilere güvenilemiyorsa, Microsoft neden sys.dm_db_index_usage_stats oluşturmayı rahatsız eder?
Craig Efrein

Değişim izleme için tasarlanmış bir DMV değildir . Performans ayarı olan amaçlanan amaç için çok güvenilirdir.
Remus Rusanu

8

Görünüşe göre oyuna iki yıl geç kaldım, ama gerçekten istediğini yapmanın oldukça hafif bir yolu var.

Size yardımcı olabilecek iki SQL Server mekanizması vardır. Nihai çözümünüz bu ikisinin bir melezi olabilir.

Değişiklik Takibi . SQL Server, belirli tabloları saatin altına yerleştirme, yalnızca hangi satırların değiştiğini (birincil anahtar değerlerine göre) ve ne tür bir değişiklik olduğunu (Ekle, Güncelle veya Sil) kaydedebilir. Bir tablo kümesinde değişiklik algılamayı ayarladıktan sonra, hafif bir sorgu size son kontrol ettiğinizden bu yana tabloda herhangi bir değişiklik yapılıp yapılmadığını söyleyebilir. Genel gider yaklaşık olarak ek bir basit endeksi korumakla aynıdır.

Rowversion / zaman damgası . Bu, bir bayt içeren veya güncellendiğinde (silme işlemine yardımcı olmaz), 8 baytlık varbinary sütun tipidir (bir BigInt'e dökülebilir), veritabanı genişliğindedir. Bu sütunları dizine eklediyseniz, MAX (zaman damgası) değerini en son değerlendirildiği zamandan bu yana karşılaştırarak satır verilerinin değişip değişmediğini kolayca anlayabilirsiniz. Değer tekdüze bir şekilde arttığından, yeni değer en son kontrol ettiğiniz değerden daha büyükse verilerin değiştiğine dair güvenilir bir gösterge sağlar.


7

Kaynak yalnızca ekleme ise bir IDENTITYsütun verin . Veri aktarımınızı yaptığınızda, üzerinde yazılan en yüksek değeri kaydedersiniz. Bir sonraki aktarım sırasında yalnızca önceki aktarım sırasında kaydedilen değerden daha yüksek değerleri sorgulamanız gerekir. Günlük kayıtlarını bir veri ambarına aktarmak için bunu yaparız.

Güncellenebilir satırlar için "kirli" bir bayrak ekleyin. Üç değeri olacaktır - temiz, kirli ve silinmiş. Günlük sorgular, bayrak "silinmiş" olarak ayarlanmış satırları atlamak zorunda kalacaktır. Bu, bakım, test ve çalışma zamanında pahalı olacaktır. Büyük sorgudan sonra, silme olarak işaretlenen tüm satırların kaldırılması ve diğerlerinin işaretinin sıfırlanması gerektiğini belirtin. Bu iyi ölçeklenmeyecek.

Veri Yakalamayı Değiştir seçeneğinin daha hafif bir alternatifi Değişiklik İzleme'dir . Hangi değerlerin değiştiğini söylemez , sadece satır en son sorgulandığından bu yana değiştiğini gösterir. Yerleşik işlevler, değişen değerlerin alınmasını ve izleme yönetimini kolaylaştırır. BT'yi 100.000.000 satırlık bir tabloda günde yaklaşık 100.000 değişikliği işlemek için başarılı olduk.

Sorgu Bildirimleri, sonuç kümesi düzeyinde daha yüksek bir kolda hareket eder. Kavramsal olarak, bir görüş tanımlamak gibidir. SQL Server, bu görünümden döndürülen herhangi bir satırın değiştiğini algılarsa, uygulamaya bir ileti gönderir. Kaç satırın değiştiğine veya hangi sütunlara ilişkin bir gösterge yoktur. "Bir şey oldu" diyen basit bir mesaj var. Sorgulamak ve tepki vermek uygulamaya kalmıştır. Tahmin edebileceğiniz gibi, pratik olarak bundan çok daha karmaşıktır. Sorgunun nasıl tanımlanabileceğine ilişkin kısıtlamalar vardır ve değiştirilen veriler dışındaki durumlar için bildirim tetiklenebilir. Bildirim tetiklendiğinde kaldırılır. Daha sonra ilgilenilen başka bir etkinlik olursa, başka bir mesaj gönderilmez.

OP sorusu bağlamında QN, kurulum için düşük ek yük ve az çalışma süresi maliyeti avantajına sahip olacaktır. Titiz bir abone-mesaj-tepki rejimi kurmak ve sürdürmek önemli bir çaba olabilir. Veri tablosu büyük olduğundan, tabloda sık sık değişiklik yapılması muhtemeldir, yani bildirim çoğu işlem döngüsünde tetiklenir. CT veya CDC'de olduğu gibi deltaların artımlı işlenmesinde neyin değiştiğine dair bir gösterge olmadığından. Yanlış tetikleme nedeniyle ek yük yorucu, ancak en kötü durumda bile pahalı sorgunun şu anda olduğundan daha sık çalıştırılması gerekmez.


3

SqlTableDependency

SqlTableDependency SQL Server veritabanında tablo kayıt değerleri içeren bildirimlere erişmek için üst düzey bir uygulama bileşenidir.

SqlTableDependency, belirtilen bir veritabanı tablosunun içeriği değiştiğinde bildirim almak için kullanılan genel bir C # bileşenidir.

.NET SqlDepenency ile arasındaki fark nedir?

Temel olarak, ana fark, SqlTableDependency'nin eklenen, değiştirilen veya silinen kayıt değerlerini içeren olayları ve tabloda yürütülen DML işleminin (ekleme / silme / güncelleme) göndermesidir: SqlDepenency, veritabanı tablosunda sadece bir şeylerin değiştiğini söylerler.

GITHUB projesine bir göz atın .


1

Beklediğiniz güncelleştirmeler bir dizini etkiliyorsa (ve yalnızca ), sys.dm_db_index_usage_statssöz konusu tablodaki bir dizindeki son güncelleştirmeyi algılamak için sistem tablosunu kullanabilirsiniz . last_user_updateAlanı kullanırdınız .

Örneğin, en son güncellenen tabloları almak için:

select
    object_name(object_id) as OBJ_NAME, *
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
order by
    dm_db_index_usage_stats.last_user_update desc

Veya belirli bir tarihten belirli bir tablonun değiştirilip değiştirilmediğini kontrol etmek için:

select
    case when count(distinct object_id) > 0 then 1 else 0 end as IS_CHANGED
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
    and object_id = object_id('MY_TABLE_NAME')
    and last_user_update > '2016-02-18'

Remus'un yukarıdaki yorumu hakkında ne düşünüyorsunuz? "Mevcut kimliğe gizlice girmek veya sys.dm_db_index_usage_stats'a bakmak gibi 'akıllı' bir çözümle hile yapmaya çalışmayın." (Ayrıca cevabının altındaki yorumuna bakınız.)
Fabian Schmied

1
@FabianSchmied İlginç - Cevabımı eklediğimde , bu kullanım örneği için güvenilir olmadığını belirtmek için Remus'un cevaplarından başka yetkili bir şey bulamadığımı görmemiştim ; için MS sayfası dm_db_index_operational_statssorunları gösterir (meta veri önbelleği temizlendiğinde temizlenir), ancak için gösterilmez dm_db_index_usage_stats. Bulduğum tek sorun, dizin yeniden oluşturma, sunucu yeniden başlatmalar ve kullanım istatistiklerini temizleyen veritabanı ayırma ile oldu ve burada uygulanan gibi görünmüyordu. Bu konuda doğrulanmış bilgileri görmek isterim.
Geoff
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.