Bir veritabanının içeriğini kontrol eden sürüm


16

Kullanıcı tarafından düzenlenebilir içerik içeren bir web projesi üzerinde çalışıyorum ve bir veritabanında yaşayan gerçek içeriğin sürüm izlemesini yapmak istiyorum. Temel olarak, wiki tarzı değişiklik geçmişlerini uygulamak istiyorum.

Bazı arka plan araştırmaları yaparken, veritabanı şemanızı nasıl güncelleyeceğinize dair çok fazla belge görüyorum (benimki zaten kontrol ediliyor), ancak veritabanı içeriği değişikliklerinizi nasıl izleyeceğinizle ilgili mevcut stratejiler , en azından şema sürümleme çığında kayboluyor aramalarımda.

Kendi değişiklik izlememi uygulamak için birkaç yol düşünebilirim, ancak hepsi oldukça kaba görünüyor:

  • Her değişiklikteki tüm satırı kaydedin, satırı Birincil anahtarla kaynak kimliğiyle ilişkilendirin (şu anda doğruya eğildiğim şey, en basitidir). Bununla birlikte, birçok küçük değişiklik bir sürü masa şişmesine neden olabilir.
  • Her değişiklik için önce / sonra / kullanıcı / zaman damgası kaydedin; değişikliği ilgili sütuna geri getirmek için bir sütun adıyla kaydedin.
  • her sütun için bir tablo ile önce / sonra / user / timestamp kaydedin (çok fazla tablo ile sonuçlanır).
  • bir sütunla her değişiklik için diffs / user / timestamp değerini kaydedin (bu, belirli bir tarihe geri dönmek için araya giren tüm değişiklik geçmişini yürümek zorunda kalacağınız anlamına gelir).

Buradaki en iyi yaklaşım nedir? Kendimi yuvarlamak muhtemelen başka birinin (daha iyi) kod tabanını yeniden icat ediyorum gibi görünüyor.


PostgreSQL için bonus puanlar.


Bu soru SO üzerinde zaten tartışılmıştır: stackoverflow.com/questions/3874199/… . Google için "veritabanı kayıt geçmişi" ve daha fazla makale bulacaksınız.
Doc Brown


Hile yapmak için neden SQL Server'ın işlem günlüğünü kullanmıyorsunuz?
Thomas Junk

Yanıtlar:


11

Normalde kullandığım teknik bir end_timestamp alanı ile tüm kaydı kaydetmek. Yalnızca bir satırın boş bir end_timestamp değerine sahip olabileceği bir iş kuralı vardır ve bu elbette şu anda aktif olan içeriktir.

Bu sistemi kullanırsanız, kuralı uygulamak için bir dizin veya kısıtlama eklemenizi önemle tavsiye ederim. Benzersiz bir dizin bir ve yalnızca bir boş içerebileceğinden, Oracle ile bu kolaydır. Diğer veritabanları daha çok sorun olabilir. Veritabanının kuralı zorunlu kılması, kodunuzu dürüst tutacaktır.

Birçok küçük değişikliğin şişkinlik yaratacağı konusunda haklısınız, ancak bunu kod ve raporlama basitliğine karşı takas etmeniz gerekiyor.


Diğer veritabanı motorlarının farklı davranabileceğini unutmayın, örn. MySQL, benzersiz dizine sahip bir sütunda birden çok NULL değerine izin verir. Bu, bu kısıtlamanın uygulanmasını çok daha zor hale getirir.
qbd

Gerçek bir zaman damgası kullanmak güvenli değildir, ancak bazı MVCC veritabanları tuples ile birlikte minimum ve maksimum işlem seri numaralarını depolayarak dahili olarak çalışır.
user2313838

"Benzersiz bir dizin bir ve yalnızca bir boş içerebileceğinden, Oracle ile bu kolaydır." Yanlış. Oracle, dizinlere hiç boş değer eklemez. Benzersiz bir dizine sahip bir sütundaki null sayısının sınırı yoktur.
Gerrat

@Gerrat Bu gereksinime sahip bir veritabanı tasarladığımdan beri birkaç yıl geçti ve artık bu veritabanına erişimim yok. Standart bir benzersiz dizinin birden çok boş değeri destekleyebildiğini haklıyorsunuz, ancak benzersiz bir kısıtlama veya muhtemelen işlevsel bir dizin kullandığımızı düşünüyorum.
kiwiron

8

Microsoft SQL Server kullanıyorsanız, Veri Yakalamayı Değiştir adlı özellik için zaten bir özellik olduğunu unutmayın . Daha sonra önceki düzeltmelere erişmek için kod yazmanız gerekecektir (CDC bunun için belirli görünümler oluşturur), ancak en azından tablolarınızın şemasını değiştirmeniz veya değişiklik izlemenin kendisini uygulamanız gerekmez.

Kaputun altında ne olur:

  • CDC, düzeltmeleri içeren ek bir tablo oluşturur,

  • Orijinal tablonuz daha önce olduğu gibi kullanılır, yani herhangi bir güncelleme doğrudan bu tabloya yansıtılır,

  • CDC tablosu yalnızca değiştirilen değerleri depolar, yani veri çoğaltması en aza indirgenir.

Değişikliklerin farklı bir tabloda depolanması iki önemli sonuç doğurur:

  • Orijinal tablodan seçimler CDC'siz kadar hızlıdır. İyi hatırlıyorsam , güncellemeden sonra CDC gerçekleşir , bu nedenle güncellemeler eşit derecede hızlıdır (ancak CDC'nin veri tutarlılığını nasıl yönettiğini iyi hatırlamıyorum).

  • Orijinal tablonun şemasında yapılan bazı değişiklikler CDC'nin kaldırılmasına yol açar. Örneğin, bir sütun eklerseniz, CDC bunun üstesinden nasıl geleceğini bilmiyor. Öte yandan, bir dizin veya kısıtlama eklemek iyi olmalıdır. Sık değişikliklere maruz kalan bir tabloda CDC'yi etkinleştirirseniz, bu hızla bir sorun haline gelir. CDC'yi kaybetmeden şemayı değiştirmeye izin veren bir çözüm olabilir, ancak aramadım.


6

Problemi önce "felsefi" olarak ve kodla çözün. Ve sonra bunu gerçekleştirmek için kod ve veritabanı ile "müzakere".

Örneğin , genel makalelerle uğraşıyorsanız, bir makale için ilk konsept şöyle görünebilir:

class Article {
  public Int32 Id;
  public String Body;
}

Ve bir sonraki en temel düzeyde, revizyonların bir listesini tutmak istiyorum:

class Article {
  public Int32 Id;
  public String Body;
  public List<String> Revisions;
}

Ve şu anki bedenin sadece en son revizyon olduğu şaşırabilir. Ve bu iki anlama geliyor: Her Revizyonun tarihlendirilmesi veya numaralandırılması gerekiyor:

class Revision {
  public Int32 Id;
  public Article ParentArticle;
  public DateTime Created;
  public String Body;
}

Ve ... ve makalenin şu anki gövdesinin en son revizyondan farklı olması gerekmiyor:

class Article {
  public Int32 Id;
  public String Body {
    get {
      return (Revisions.OrderByDesc(r => r.Created))[0];
    }
    set {
      Revisions.Add(new Revision(value));
    }
  }
  public List<Revision> Revisions;
}

Birkaç ayrıntı eksik; ancak muhtemelen iki varlık istediğinizi gösterir . Biri makaleyi (veya diğer başlık türünü) temsil eder ve diğeri revizyonların bir listesidir (alanların gruplandırılması iyi "felsefi" mantıklı olan gruplama). Başlangıçta özel veritabanı kısıtlamalarına ihtiyacınız yoktur, çünkü kodunuz kendi başlarına yapılan revizyonları umursamıyor - bunlar revizyonları bilen bir makalenin özellikleridir.

Bu nedenle, "geçerli" makaleyi işaretlemek için revizyonları özel bir şekilde işaretleme veya veritabanı kısıtlamasına yaslanma konusunda endişelenmenize gerek yoktur. Sadece zaman damgası (otomatik olarak girilen bir kimlik bile iyi olurdu), ana makaleleriyle ilgili hale getirin ve makalenin "en son" olanın en alakalı olanı bilmesinden sorumlu olmasını sağlayın.

Ayrıca, bir ORM'in daha az felsefi ayrıntıları işlemesine izin verirsiniz veya hazır bir ORM kullanmıyorsanız bunları özel bir yardımcı program sınıfında gizlersiniz.

Çok daha sonra, bazı stres testleri yaptıktan sonra, bu revizyon özelliğini tembel yük haline getirmeyi veya Body özniteliğinizin tembel yükü yalnızca en üst revizyona almayı düşünebilirsiniz. Ancak, bu durumda veri yapınızın bu optimizasyonlara uyacak şekilde değiştirilmesi gerekmez.


2

Bir denetim izleme tetikleyicisi için, ihtiyacınız olanı yapacak bir denetim günlüğü oluşturma konusunda size yol gösteren bir PostgreSQL wiki sayfası vardır .

Bir değişikliğin tüm orijinal verilerini ve güncellemeler için yeni değerlerin listesini izler (ekleme ve silme işlemleri için yalnızca bir değer vardır). Eski bir sürümü geri yüklemek isterseniz, orijinal verilerin kopyasını denetim kaydından alabilirsiniz. Verileriniz yabancı anahtarlar içeriyorsa, tutarlılık sağlamak için bu kayıtların da geri alınması gerekebileceğini unutmayın.

Genel olarak konuşursak, veritabanı uygulamanız zamanının çoğunu sadece güncel verilere harcıyorsa, alternatif verileri geçerli verilerden ayrı bir tabloda izlemekten daha iyi olduğunu düşünüyorum. Bu, aktif tablo indekslerinizi daha yönetilebilir tutacaktır.

İzlediğiniz satırlar çok büyükse ve alan ciddi bir endişe ise, değişiklikleri yıkmaya ve minimum fark / yama depolamaya çalışabilirsiniz, ancak bu kesinlikle her türlü veri türünü kapsamak için daha fazla iştir. Bunu daha önce yaptım ve tüm değişikliklerin geriye doğru teker teker geçerek eski veri sürümlerini yeniden oluşturmak acı vericiydi.


1

Bir satırın eski sürümünü tablo başına geçmiş günlüğüne kopyalayan bir tetikleyici olan en basit seçenekle devam ettim.

Çok fazla veritabanı şişkinliği ile sarılırsam, gerekirse küçük tarih değişikliklerinin bazılarını daraltmaya bakabilirim.

Tetikleyici fonksiyonları otomatik olarak oluşturmak istediğim için çözüm oldukça dağınık bir şekilde yaralandı. Ben SQLAlchemy, bu yüzden güzel olan bazı miras hijinks yaparak tarih tablosunu üretmek mümkün, ancak gerçek tetik işlevleri düzgün düzgün PostgreSQL fonksiyonları oluşturmak için bazı dize munging gerektiren ve bir tablodan sütunları eşleştirmek için yara başka bir doğru.

Her neyse, hepsi github burada .

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.