Olay Günlüğü (Event Log) Tasarımı: Rapor Değil Ham Olay Saklayın

Olay Günlüğü (Event Log) Tasarımı: Rapor Değil Ham Olay Saklayın

Sadece toplamları saklayan sistemler, yeni bir soru sorulduğunda cevapsız kalır. Ham olay saklamak geleceği açık tutar.

Bir olay günlüğü, sistemde gerçekleşen her anlamlı olayı olduğu gibi, özetlenmemiş biçimde saklayan tablodur. Çoğu proje bunun yerine doğrudan toplam tablolar kurar: "günlük sipariş sayısı", "aylık ciro". Bu tablolar mevcut soruları hızlı cevaplar, ama yeni bir soru geldiğinde — "kampanya kodu kullananların ikinci siparişi ne zaman geldi?" — elinizde veri kalmaz.

Ham olay ve toplam saklamanın karşılaştırması
Toplam bir cevaptır; ham olay ise henüz sorulmamış soruların cevabıdır.

Olay şeması: değişmeyen çekirdek

İyi bir olay kaydı beş soruya cevap verir: ne oldu, ne zaman oldu, kim yaptı, neyle ilgili, hangi bağlamda.

SQL
CREATE TABLE olaylar (
  id            BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  olay_tipi     VARCHAR(60)  NOT NULL,          -- siparis.olusturuldu
  olay_surumu   TINYINT      NOT NULL DEFAULT 1,-- şema evrilirse artar
  olay_anahtari CHAR(36)     NOT NULL,          -- üretici tarafından, tekrarı engeller
  gerceklesti   DATETIME(3)  NOT NULL,          -- OLAYIN gerçekleştiği an
  kaydedildi    DATETIME(3)  NOT NULL,          -- bize ULAŞTIĞI an
  aktor_tipi    ENUM('kullanici','sistem','entegrasyon') NOT NULL,
  aktor_id      BIGINT UNSIGNED NULL,
  varlik_tipi   VARCHAR(40)  NOT NULL,          -- siparis, urun, kullanici
  varlik_id     BIGINT UNSIGNED NOT NULL,
  yuk           JSON         NOT NULL,          -- tipe özgü alanlar
  UNIQUE KEY uq_anahtar (olay_anahtari),
  KEY ix_tip_zaman (olay_tipi, gerceklesti),
  KEY ix_varlik (varlik_tipi, varlik_id, gerceklesti)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

İki tarih alanının ayrı tutulması kritiktir. gerceklesti iş anlamını, kaydedildi ise teknik geliş anını taşır. Mobil uygulama çevrimdışıyken biriken olaylar üç saat sonra geldiğinde bu ayrım olmadan rapor kayar.

Yazma tarafı: tekrarı en baştan engelleyin

Olay üreten taraf, her olay için benzersiz bir anahtar üretmelidir. Böylece aynı olay iki kez gönderilse bile veritabanı ikinciyi reddeder — bu, dağıtık sistemlerde kaçınılmaz olan tekrarı zararsız kılar.

PHP
final class OlayYazici
{
    public function __construct(private \CodeIgniter\Database\BaseConnection $db) {}

    public function yaz(string $tip, string $varlikTipi, int $varlikId, array $yuk, ?int $aktorId = null): void
    {
        $satir = [
            'olay_tipi'     => $tip,
            'olay_surumu'   => 1,
            'olay_anahtari' => $this->anahtar($tip, $varlikId, $yuk),
            'gerceklesti'   => $yuk['_an'] ?? date('Y-m-d H:i:s.v'),
            'kaydedildi'    => date('Y-m-d H:i:s.v'),
            'aktor_tipi'    => $aktorId ? 'kullanici' : 'sistem',
            'aktor_id'      => $aktorId,
            'varlik_tipi'   => $varlikTipi,
            'varlik_id'     => $varlikId,
            'yuk'           => json_encode($yuk, JSON_UNESCAPED_UNICODE),
        ];

        // IGNORE: aynı anahtarla ikinci kez gelirse sessizce atla (idempotent yazma)
        $this->db->query(
            'INSERT IGNORE INTO olaylar (olay_tipi, olay_surumu, olay_anahtari, gerceklesti, kaydedildi,
             aktor_tipi, aktor_id, varlik_tipi, varlik_id, yuk) VALUES (?,?,?,?,?,?,?,?,?,?)',
            array_values($satir)
        );
    }

    private function anahtar(string $tip, int $varlikId, array $yuk): string
    {
        return $yuk['_anahtar'] ?? sprintf('%s-%d-%s', $tip, $varlikId, $yuk['_an'] ?? microtime(true));
    }
}

Okuma tarafı: toplamlar türetilir, saklanmaz

Toplam tabloları silmeniz gerekmez — ama onları kaynak değil türev olarak görmelisiniz. Hatalı bir olay düzeltildiğinde toplam yeniden hesaplanabilir.

SQL
-- Günlük özet: ham olaydan türetilir, gerektiğinde yeniden kurulur
INSERT INTO ozet_gunluk_siparis (gun, adet, ciro_kurus)
SELECT DATE(gerceklesti)                        AS gun,
       COUNT(*)                                 AS adet,
       SUM(CAST(JSON_EXTRACT(yuk, '$.tutar_kurus') AS UNSIGNED)) AS ciro_kurus
FROM olaylar
WHERE olay_tipi = 'siparis.olusturuldu'
  AND gerceklesti >= ? AND gerceklesti < ?
GROUP BY DATE(gerceklesti)
ON DUPLICATE KEY UPDATE adet = VALUES(adet), ciro_kurus = VALUES(ciro_kurus);

Özet tabloyu her zaman yeniden çalıştırılabilir yazın (ON DUPLICATE KEY UPDATE ya da önce sil-sonra yaz). Böylece geç gelen olaylar için aynı günü tekrar işlemek güvenli olur.

Şema evrimi: sürüm alanı neden var?

Olay tipleri zamanla değişir; bir alan eklenir, bir alanın anlamı kayar. Geçmiş kayıtları geriye dönük değiştirmek olay günlüğünün temel ilkesine aykırıdır. Bunun yerine olay_surumu artırılır ve okuma tarafı iki sürümü de anlar. Metrik tanımlarının ekipler arasında ayrışmaması içinse ayrı bir disiplin gerekir; bunu şirket içi veri sözlüğü yazımızda ele almıştık.

Yeni katılan ekip üyelerinin bu şemayı hızla kavraması için kod okuyarak öğrenme yöntemimizi öneriyoruz.

Gizlilik: ham veri sınırsız saklanmaz

Ham olay saklamanın bedeli yalnızca depolama değildir; kişisel veri saklıyorsanız saklama süresi ve amaç sınırlaması yükümlülüğü doğar. Yükte kimlik verisini doğrudan taşımak yerine kullanıcı kimliğine referans vermek ve saklama süresi politikası tanımlamak gerekir. Konunun hukuki tarafı için yapay zekâ projelerinde veri gizliliği yazımıza, veri kültürü tarafı için veri odaklı karar kültürü yazımıza bakabilirsiniz. JSON alanlarıyla çalışırken MySQL JSON belgeleri indeksleme seçeneklerini de anlatır.

Sonuç

Toplam tablolar bugünün sorularını hızlı cevaplar; ham olay günlüğü ise yarının sorularını mümkün kılar. Tasarımın omurgası nettir: her olayın benzersiz anahtarı olsun, gerçekleşme ve kaydedilme anları ayrı tutulsun, yük JSON olarak esnek kalsın, şema sürümlensin ve toplamlar her zaman yeniden üretilebilir olsun. Bir sonraki raporlama isteğinde "bu veriyi tutmuyoruz" cevabını vermek zorunda kalmamak için, olayları bugünden ham hâliyle saklamaya başlayın — depolama en ucuz kalemdir, kaybedilen veri ise geri gelmez.

Beğeniniz benzer içerikleri öne çıkarmamıza yardımcı olur.

Sık Sorulan Sorular

Şişirir ve bu beklenen bir maliyettir. Yönetimi için üç araç vardır: tarihe göre bölümleme, soğuk verinin arşive taşınması ve saklama süresi politikası. Depolama maliyeti, kaybedilen analiz imkânının yanında genelde küçük kalır.

Amaçları farklıdır. Denetim izi hukuki ve güvenlik gereksinimi için "kim neyi değiştirdi" sorusuna cevap verir ve değiştirilemez olmalıdır. Olay günlüğü ise analitik amaçlıdır. Pratikte ortak bir altyapı kullanabilirler, ama saklama süreleri ve erişim kuralları ayrışır.

Gerçekleşme ve kaydedilme anlarını ayrı tuttuysanız bozmaz. Raporu gerçekleşme anına göre üretir, geç gelen kayıtlar için ilgili günü yeniden hesaplarsınız. Bu yüzden özet üretim işleri mutlaka yeniden çalıştırılabilir yazılmalıdır.

Çok kullanılan birkaç alanı ayrı sütuna çıkarmak (ve indekslemek) meşru bir optimizasyondur. Ancak her olay tipinin farklı alanları olduğu için hepsini sütunlaştırmak, sürekli şema değişikliği gerektiren kırılgan bir tablo üretir. Karma yaklaşım genelde en dengeli olanıdır.

S
superadmin

Bu yazıyı hazırladı. Sorularınız için iletişim sayfasından ulaşabilirsiniz.

Yorumlar (0)

Bu yazıya henüz yorum yapılmamış. İlk yorumu siz yazın!

Yorum Yaz

Yorumunuz onaylandıktan sonra yayınlanır. Ekibimiz gerekirse konuyla ilgili bir yanıt da paylaşır.

En az 10 karakter.