Denetim günlüğü için veritabanı tasarımı


153

Her yeni veritabanı tasarlamam gerektiğinde, değişikliklerin bir denetim günlüğünü tutmak için veritabanı şemasını nasıl kurmam gerektiğini düşünerek epeyce zaman harcıyorum.

Burada bununla ilgili bazı sorular sorulmuştu, ancak tüm senaryolar için tek bir en iyi yaklaşım olduğu konusunda hemfikir değilim:

Ayrıca , her bir yaklaşımın artılarını ve eksilerini listelemeye çalışan Veritabanı Değişikliklerinin Günlüğünü Koruma hakkındaki bu ilginç makaleye de rastladım . Çok iyi yazılmış ve ilginç bilgiler içeriyor, ancak kararlarımı daha da zorlaştırdı.

Sorum şu: Kullanabileceğim bir referans var mı, belki bir kitap veya bir karar ağacı gibi, bazı girdi değişkenlerine göre hangi yöne gitmem gerektiğine karar vermek için başvurabileceğim bir şey var mı, örneğin:

  • Veritabanı şemasının olgunluğu
  • Günlükler nasıl sorgulanacak
  • Kayıtların yeniden oluşturulması gerekme olasılığı
  • Daha da önemlisi: yazma veya okuma performansı
  • Günlüğe kaydedilen değerlerin niteliği (dize, sayılar, bloblar)
  • Mevcut depolama alanı

Bildiğim yaklaşımlar:

1. Oluşturma ve değiştirme tarihi ve kullanıcı için sütunlar ekleyin

Tablo örneği:

  • İD
  • değer_1
  • değer_2
  • değer_3
  • create_date
  • modifiye_tarihi
  • tarafından yaratıldı
  • tarafından tasarlandı

Başlıca eksiler: Değişikliklerin geçmişini kaybediyoruz. Kaydetmeden sonra geri alınamaz.

2. Yalnızca tabloları ekleyin

Tablo örneği :

  • İD
  • değer_1
  • değer_2
  • değer_3
  • itibaren
  • -e
  • silinmiş (Boole)
  • kullanıcı

Başlıca eksiler: Yabancı anahtarlar nasıl güncel tutulur? Büyük alan gerekli

3. Her tablo için ayrı bir geçmiş tablosu oluşturun

Geçmiş tablosu örneği:

  • İD
  • değer_1
  • değer_2
  • değer_3
  • değer_4
  • kullanıcı
  • silinmiş (Boole)
  • zaman damgası

Başlıca eksiler: Denetlenen tüm tabloların kopyalanması gerekir. Şema değişirse, tüm günlüklerin de taşınması gerekecektir.

4. Tüm Tablolar için Birleştirilmiş bir geçmiş Tablosu oluşturun

Geçmiş tablosu örneği:

  • Tablo ismi
  • alan
  • kullanıcı
  • yeni değer
  • silinmiş (Boole)
  • zaman damgası

Başlıca eksiler: Gerekirse kayıtları kolayca yeniden oluşturabilecek miyim (geri alma)? New_value sütununun tüm farklı sütun türlerini destekleyebilmesi için büyük bir dizge olması gerekir.



1
ve tablolar yerine bir geçmiş veritabanı kullanmaya ne dersiniz?
Jowen


Yürütülen tüm (gerekli) sorguları olduğu gibi kaydetmek kötü bir fikir mi?
Dinushan

Yanıtlar:


87

Birkaç wiki platformu tarafından kullanılan yöntemlerden biri, tanımlayıcı verileri ve denetlemekte olduğunuz içeriği ayırmaktır. Karmaşıklık katar, ancak kullanıcıya eski kaydın neye benzediğine dair bir fikir vermek için karıştırmanız gereken yalnızca düzenlenmiş alanların listelerini değil, eksiksiz kayıtların bir denetim izini elde edersiniz.

Örneğin, satış anlaşmalarını izlemek için Fırsatlar adlı bir tablonuz olsaydı, aslında iki ayrı tablo oluşturursunuz:

Opportunities
Opportunities_Content (veya bunun gibi bir şey)

Fırsatlar tablo kaydı benzersiz tanımlamak için kullandığımız ve size yabancı anahtar ilişkileri için referans ediyorum birincil anahtar evi olacaktır bilgiye sahip olacaktır. Opportunities_Content tablo kullanıcılarının değiştirebilir tüm alanları yapacağını ve kendisi için bir denetim izi kalmasını isterim. İçerik tablosundaki her kayıt , kendi PK'sini ve değiştirilen ve değiştirilen tarih verilerini içerir. Fırsatlar tablo ana kayıt aslen ve kim tarafından oluşturulduğu zaman güncel bir sürüme başvuru yanı sıra bilgi de içerecek.

İşte basit bir örnek:

CREATE TABLE dbo.Page(  
    ID int PRIMARY KEY,  
    Name nvarchar(200) NOT NULL,  
    CreatedByName nvarchar(100) NOT NULL, 
    CurrentRevision int NOT NULL, 
    CreatedDateTime datetime NOT NULL

Ve içerik:

CREATE TABLE dbo.PageContent(
    PageID int NOT NULL,
    Revision int NOT NULL,
    Title nvarchar(200) NOT NULL,
    User nvarchar(100) NOT NULL,
    LastModified datetime NOT NULL,
    Comment nvarchar(300) NULL,
    Content nvarchar(max) NOT NULL,
    Description nvarchar(200) NULL

Muhtemelen içindekiler tablosunun PK'sini PageID'den çok sütunlu bir anahtar yapardım ve Revizyonun bir kimlik türü olması koşuluyla Revizyon. FK olarak Revizyon sütununu kullanırsınız. Daha sonra birleştirilmiş kaydı şu şekilde KATILARAK çekersiniz:

SELECT * FROM Page
JOIN PageContent ON CurrentRevision = Revision AND ID = PageID

Orada bazı hatalar olabilir ... bu aklımın ucundan bile geçmiyor. Yine de size alternatif bir model hakkında bir fikir vermelidir.


10
İyi denetim yaklaşımı açısından, ancak üretim için, veritabanındaki her tablo için ayrı bir denetim tablosu geliştirmek, değişiklikleri yakalamak için her tablo için tetikleyiciler yazmak ve bunu denetim tablosuna yazmak çok zaman alacaktır. Ayrıca, her denetim tablosunun yapısı farklı olduğundan, tüm tablolar için tek bir denetim raporu geliştirmede büyük bir zorluktur.
asim-ishaq

12
Her tablo için komut dosyası yazmak ve sürdürmek, denetlenen bir veritabanını yönetmeyi amaçlayan bir kuruluş için bir endişe kaynağıysa, doğal olarak deneyimli bir DBA'yı veya denetlenmiş veritabanları oluşturmak için yeterli deneyime sahip oldukça esnek ve çok deneyimli bir yazılım mühendisini işe almalarını tavsiye ederim. .
Hardryv

1
O düzeltmek mi PageContent.PageIDetmek FK olduğunu Page.IDve Page.CurrentRevisionFK etmektir PageContent.Revision? Bu bağımlılık gerçekten döngüsel mi?

2
Bahsedilen alternatiflere değinmediği için oy verdim. Çok özel bir kullanım durumu için çok özel bir çözüm olan başka bir seçenek sunar. Ama önerilen tasarımın esasını görüyorum
acteon

1
Güvenle söyleyebileceğim çok az alan düşünebiliyorum, bu yüzden her varlık için tüm "ana" tablolar sadece var olacak id, revision_id; daha çok bir bağlantı masası, gerçekten. Bu bana biraz kokuyor. Bunun OP'deki yaklaşım 3'e göre ne gibi bir avantajı var (denetlenen tablo başına geçmiş tablosu)?
Kenmore

14

SQL Server 2008 kullanıyorsanız, muhtemelen Veri Yakalamayı Değiştir'i düşünmelisiniz. Bu, 2008 için yenidir ve size önemli miktarda iş tasarrufu sağlayabilir.


İşte SQL 2012 değişiklik izleme bilgisinin bağlantısı. msdn.microsoft.com/en-us/library/bb933994.aspx +1, tekerleği yeniden icat etmenin hiçbir anlamı olmayan yerleşik işlevselliği kullanmak için.
Chris

5
@Chris hiç kendin kullandın mı? Aslında, her şeyi izliyor ... ama ondan faydalı bilgiler alabilmek tamamen başka bir hikaye. Bisikletim için traktör tekerleği kullanamam.
Jowen

Bu gerçekten harika olurdu . Ancak , benim gibi SQL Server'ın yalnızca Standard sürümünü kullanıyorsanız, şansınız kalmaz: "Değişiklik verileri yakalama yalnızca Enterprise , Developer ve Enterprise Evaluation sürümlerinde mevcuttur".
Brad Turek

Değişiklik Verisi Yakalamanın, WHO'nun bazı değişiklikler yaptığı gibi temel bilgileri aktarmanıza izin vermediğini belirtmek önemlidir . Bu bilgiyi yakalamak için bir tetikleyici oluşturmanız gerekir. Kanımca, tetikleyicilere güvenmeniz gerektiğinden, herhangi bir SQL Server sürümünde çalışan kendi denetleme tetikleyicilerinize sahip olmak daha mantıklıdır.
drizin

6

Herhangi bir referans bilmiyorum ama eminim ki birisi bir şeyler yazmıştır.

Bununla birlikte, amaç sadece ne olduğunun bir kaydını tutmaksa (bir denetim günlüğünün en tipik kullanımı) o zaman neden her şeyi saklamayasınız:

timestamp
username
ip_address
procedureName (if called from a stored procedure)
database
table
field
accesstype (insert, delete, modify)
oldvalue
newvalue

Muhtemelen bu bir tetikleyici tarafından korunur.


Bunu veritabanı sunucusundan almanın herhangi bir yolunu bilmiyorum, ama elbette bu, onun dışından kolayca yapılabilir.
wallyk

5
Bana öyle geliyor ki, bu orijinal soruda gösterilen 4. seçenekle aynı tasarım modeli.
givanse

4

Bir blog uygulaması için küçük bir örnek veritabanı oluşturacağız. İki tablo gereklidir:

blog: benzersiz bir gönderi kimliği, başlık, içerik ve silinmiş bir bayrak saklar. audit: bir kayıt kimliği, blog gönderisi kimliği, değişiklik türü (YENİ, DÜZENLE veya SİL) ve bu değişikliğin tarih / saatiyle temel bir tarihsel değişiklik kümesini saklar. Aşağıdaki SQL blog, silinen sütunu oluşturur ve dizine ekler:

CREATE TABLE `blog` (
    `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
    `title` text,
    `content` text,
    `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`),
    KEY `ix_deleted` (`deleted`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='Blog posts';

Aşağıdaki SQL audittabloyu oluşturur . Tüm sütunlar dizine alınır ve denetim.blog_id için blog.id'ye başvuran bir yabancı anahtar tanımlanır. Bu nedenle, bir blog girişini fiziksel olarak SİLDİĞİMİZ zaman, tam denetim geçmişi de kaldırılır.

CREATE TABLE `audit` (
    `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
    `blog_id` mediumint(8) unsigned NOT NULL,
    `changetype` enum('NEW','EDIT','DELETE') NOT NULL,
    `changetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `ix_blog_id` (`blog_id`),
    KEY `ix_changetype` (`changetype`),
    KEY `ix_changetime` (`changetime`),
    CONSTRAINT `FK_audit_blog_id` FOREIGN KEY (`blog_id`) REFERENCES `blog` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2

Karar ağacı gibisi olmadığını düşünüyorum. Bazı artılar ve eksiler (veya gereksinimler) gerçekten sayılabilir olmadığından. Örneğin, Olgunluğu nasıl ölçüyorsunuz?

Bu nedenle, denetim günlüğünüz için iş gereksinimlerinizi sıralayın. Bu gereksinimlerin gelecekte nasıl değişebileceğini tahmin etmeye çalışın ve teknik gereksinimlerinizi oluşturun. Şimdi bunu artıları ve eksileri ile karşılaştırabilir ve doğru / en iyi seçeneği seçebilirsiniz.

Ve emin olun, nasıl karar verdiğiniz önemli değil, her zaman yanlış karar verdiğinizi düşünen birileri olacaktır. Ancak ödevinizi yaptınız ve kararınızı haklı çıkardınız.

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.