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.

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.
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.
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.
-- 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.
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.
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.