RDBMS'yi olay kaynaklı depolama olarak kullanma


119

Olay kaynaklı verileri depolamak için bir RDBMS (örn. SQL Sunucusu) kullanıyor olsaydım, şema nasıl görünebilirdi?

Soyut anlamda konuşulan birkaç varyasyon gördüm, ama somut hiçbir şey yok.

Örneğin, bir kişinin "Ürün" varlığına sahip olduğunu ve bu üründe yapılan değişikliklerin şu şekilde olabileceğini varsayalım: Fiyat, Maliyet ve Açıklama. Şunu yapıp yapmayacağım konusunda kafam karıştı:

  1. Bir ürün için tüm alanları içeren bir "ProductEvent" tablosuna sahip olun, burada her değişikliğin o tabloda yeni bir kayıt anlamına geldiği ve buna ek olarak "kim, ne, nerede, neden, ne zaman ve nasıl" (WWWWWH)). Maliyet, fiyat veya açıklama değiştirildiğinde, Ürünü temsil etmek için yepyeni bir satır eklenir.
  2. Ürün Maliyetini, Fiyatını ve Açıklamasını, yabancı anahtar ilişkisiyle Ürün tablosuna birleştirilmiş ayrı tablolarda depolayın. Bu özelliklerde değişiklik olduğunda, uygun şekilde WWWWWH ile yeni satırlar yazın.
  3. WWWWWH'yi ve olayı temsil eden serileştirilmiş bir nesneyi bir "ProductEvent" tablosunda depolayın; bu, belirli bir Ürün için uygulama durumunu yeniden oluşturmak için olayın kendisinin yüklenmesi, serileştirilmesi ve yeniden oynatılması gerektiği anlamına gelir .

Özellikle yukarıdaki 2. seçenek için endişeleniyorum. En uç noktaya bakıldığında, ürün tablosu, mülk başına neredeyse bir tablo olacaktır; belirli bir ürün için Uygulama Durumunun nereye yükleneceği, her ürün olay tablosundan o ürün için tüm olayların yüklenmesini gerektirecektir. Bu masa patlaması bana yanlış kokuyor.

Eminim "duruma göre değişir" ve tek bir "doğru cevap" olmasa da, neyin kabul edilebilir olduğu ve neyin tamamen kabul edilemez olduğu konusunda bir fikir edinmeye çalışıyorum. Ayrıca NoSQL'in burada yardımcı olabileceğinin de farkındayım, burada olayların bir toplu köke karşı depolanabileceği, yani nesneyi yeniden oluşturmak için olayları almak için veritabanına yalnızca tek bir istek var, ancak burada bir NoSQL veritabanı kullanmıyoruz. an bu yüzden alternatifler için etrafta hissediyorum.


2
En basit haliyle: [Event] {AggregateId, AggregateVersion, EventPayload}. Toplama türüne gerek yoktur, ancak isteğe bağlı olarak depolayabilirsiniz. Etkinlik türüne gerek yoktur, ancak isteğe bağlı olarak saklayabilirsiniz. Olanların uzun bir listesi, diğer her şey sadece optimizasyon.
Yves Reynhout

7
# 1 ve # 2'den kesinlikle uzak durun. Her şeyi bir blob olarak seri hale getirin ve bu şekilde depolayın.
Jonathan Oliver

Yanıtlar:


109

Olay deposunun, olayların belirli alanları veya özellikleri hakkında bilgi sahibi olmasına gerek yoktur. Aksi takdirde, modelinizdeki her değişiklik, veritabanınızı taşımak zorunda kalmanıza neden olur (eski moda durum tabanlı kalıcılıkta olduğu gibi). Bu nedenle 1. ve 2. seçeneği hiç önermem.

Ncqrs'da kullanılan şema aşağıdadır . Gördüğünüz gibi "Events" tablosu ilgili verileri bir CLOB (yani JSON veya XML) olarak depolar. Bu, 3. seçeneğinize karşılık gelir (Yalnızca bir genel "Olaylar" tablosuna ihtiyacınız olduğu için "ProductEvents" tablosu yoktur. Ncqrs'de Toplama Köklerinize eşleme, her EventSource'un bir gerçek Toplu Kök.)

Table Events:
    Id [uniqueidentifier] NOT NULL,
    TimeStamp [datetime] NOT NULL,

    Name [varchar](max) NOT NULL,
    Version [varchar](max) NOT NULL,

    EventSourceId [uniqueidentifier] NOT NULL,
    Sequence [bigint], 

    Data [nvarchar](max) NOT NULL

Table EventSources:
    Id [uniqueidentifier] NOT NULL, 
    Type [nvarchar](255) NOT NULL, 
    Version [int] NOT NULL

Jonathan Oliver'ın Etkinlik Deposu uygulamasının SQL kalıcılık mekanizması temelde bir BLOB alanı "Yük" ile "İşlemler" adlı bir tablodan oluşur. Bu, Ncqrs'dekiyle hemen hemen aynıdır, yalnızca olayın özelliklerini ikili biçimde (örneğin şifreleme desteği ekler) serileştirir.

Greg Young , Greg'in web sitesinde kapsamlı bir şekilde belgelendiği üzere benzer bir yaklaşım önermektedir .

Prototipik "Olaylar" tablosunun şeması şöyledir:

Table Events
    AggregateId [Guid],
    Data [Blob],
    SequenceNumber [Long],
    Version [Int]

9
Güzel cevap! EventSourcing'i kullanmak için okumaya devam ettiğim ana argümanlardan biri, geçmişi sorgulama yeteneğidir. Tüm ilginç veriler XML veya JSON olarak serileştirildiğinde sorgulamada verimli bir raporlama aracı nasıl yapacağım? Tablo tabanlı bir çözüm arayan ilginç makaleler var mı?
Marijn Huizendveld

11
@MarijnHuizendveld, muhtemelen olay deposunun kendisini sorgulamak istemezsiniz. En yaygın çözüm, olayları bir raporlama veya BI veritabanına yansıtan birkaç olay işleyiciyi bağlamaktır. Olay geçmişini bu işleyicilere karşı tekrar oynatın.
Dennis Traub

1
@Denis Traub cevabınız için teşekkürler. Neden olay deposunun kendisine karşı sorgulama yapmıyorsunuz? Korkarım yeni bir iş zekası vakası bulduğumuz her seferde tüm geçmişi yeniden oynatmak zorunda kalırsak, oldukça dağınık / yoğun olacak mı?
Marijn Huizendveld

1
Modeldeki verileri en son durumunda depolamak için bir noktada etkinlik deposunun yanı sıra tablolarınız olması gerektiğini düşündüm. Ve modeli bir okuma modeli ve bir yazma modeli olarak böldünüz. Yazma modeli olay deposuna aykırıdır ve olay deposu dövüşçüleri okuma modeline günceller. Okuma modeli, sisteminizdeki varlıkları temsil eden tabloları içerir - böylece raporlama ve görüntüleme yapmak için okuma modelini kullanabilirsiniz. Bir şeyi yanlış anlamış olmalıyım.
theBoringCoder

10
@theBoringCoder Görünüşe göre Event Sourcing ve CQRS kafanız karışmış veya en azından kafanızda ezilmiş. Sıklıkla birlikte bulunurlar ama aynı şey değildirler. CQRS, okuma ve yazma modellerinizi ayırmanızı sağlarken, Event Sourcing, uygulamanızda tek doğruluk kaynağı olarak bir olay akışını kullanmanızı sağlar.
Bryan Anderson

7

GitHub projesi CQRS.NET , birkaç farklı teknolojide EventStores'ı nasıl yapabileceğinize dair birkaç somut örnek içerir. Yazma sırasında SQL'de Linq2SQL kullanan bir uygulama ve onunla birlikte gitmek için bir SQL şeması var , biri MongoDB için , biri DocumentDB için (Azure'daysanız CosmosDB) ve EventStore'u kullanıyor (yukarıda belirtildiği gibi). Azure'da düz dosya depolamaya çok benzeyen Tablo Depolama ve Blob depolama gibi daha fazlası var.

Sanırım buradaki ana nokta, hepsinin aynı ilkeye / sözleşmeye uymasıdır. Hepsi bilgiyi tek bir yerde / kapta / tabloda depolar, bir olayı diğerinden tanımlamak için meta verileri kullanırlar ve tüm olayı olduğu gibi 'sadece' saklarlar - bazı durumlarda, destekleyici teknolojilerde olduğu gibi serileştirilmiştir. Dolayısıyla, bir belge veritabanı, ilişkisel veritabanı veya hatta düz dosya seçmenize bağlı olarak, bir etkinlik deposunun aynı amacına ulaşmanın birkaç farklı yolu vardır (herhangi bir noktada fikrinizi değiştirirseniz ve geçiş yapmanız veya desteklemeniz gerektiğini fark ederseniz yararlıdır. birden fazla depolama teknolojisi).

Projede bir geliştirici olarak, yaptığımız seçimlerden bazıları hakkında bazı görüşler paylaşabilirim.

Öncelikle bulduk (tamsayılar yerine benzersiz UUID'ler / GUID'ler ile bile) birçok nedenden ötürü sıralı kimlikler stratejik nedenlerden dolayı ortaya çıkıyor, bu nedenle bir kimliğe sahip olmak bir anahtar için yeterince benzersiz değildi, bu yüzden ana kimlik anahtarı sütunumuzu verilerle birleştirdik / gerçekten (uygulamanız anlamında) benzersiz bir anahtar olması gereken şeyi oluşturmak için nesne türü. Bazılarının onu depolamanıza gerek olmadığını söylediğini biliyorum, ancak bu, yeşil alan olup olmadığınıza veya mevcut sistemlerle birlikte var olmak zorunda olup olmadığınıza bağlı olacaktır.

Sürdürülebilirlik nedenleriyle tek bir kap / tablo / koleksiyonla kaldık, ancak varlık / nesne başına ayrı bir tabloyla oynadık. Uygulamada, uygulamanın "OLUŞTURMA" izinlerine ihtiyaç duyduğu anlamına geldiğini gördük (ki bu genellikle iyi bir fikir değildir ... genel olarak, her zaman istisnalar / istisnalar vardır) veya her yeni varlık / nesne ortaya çıktığında veya dağıtıldığında, yeni saklama kapları / masaları / koleksiyonlarının yapılması gerekiyor. Bunun yerel geliştirme için çok yavaş ve üretim dağıtımları için sorunlu olduğunu gördük. Olmayabilirsin, ama bu bizim gerçek dünya deneyimimizdi.

Hatırlanması gereken başka bir şey de, X eyleminin gerçekleşmesini istemenin birçok farklı olayın meydana gelmesine neden olabileceğidir, böylece bir komut / olay tarafından oluşturulan tüm olayları / neyin yararlı olduğunu bilir. Aynı zamanda farklı nesne türlerinde de olabilirler, örneğin bir alışveriş sepetinde "satın al" seçeneğine basmak, hesabı ve depolama olaylarını tetikleyebilir. Tüketen bir uygulama tüm bunları bilmek isteyebilir, bu nedenle bir CorrelationId ekledik. Bu, bir tüketicinin taleplerinin bir sonucu olarak ortaya çıkan tüm olayları isteyebileceği anlamına geliyordu. Bunu şemada göreceksiniz .

Özellikle SQL ile, dizinler ve bölümler yeterince kullanılmadığında performansın gerçekten bir darboğaz haline geldiğini gördük. Anlık görüntüleri kullanıyorsanız olayların ters sırada akışının yapılması gerektiğini unutmayın. Birkaç farklı dizin denedik ve pratikte, üretim içi gerçek dünya uygulamalarında hata ayıklamak için bazı ek dizinlere ihtiyaç olduğunu gördük. Yine bunu şemada göreceksiniz .

Diğer üretim içi meta veriler, üretim tabanlı araştırmalar sırasında yararlıydı; zaman damgaları, olayların devam ettirildiği ve yükseltildiği sırayla ilgili bize fikir verdi. Bu bize, büyük miktarlarda olayı ortaya çıkaran, özellikle ağır olay güdümlü bir sistemde biraz yardım sağladı ve bize ağlar ve ağ üzerindeki sistem dağıtımı gibi şeylerin performansı hakkında bilgi verdi.


Bu harika teşekkürler. Olduğu gibi, bu soruyu yazdığımdan beri uzun süredir, github'daki Inforigami.Regalo kütüphanemin bir parçası olarak kendime birkaç tane oluşturdum. RavenDB, SQL Server ve EventStore uygulamaları. Gülmek için dosya tabanlı bir tane yapmayı merak ettim. :)
Neil Barnwell

1
Şerefe. Yanıtı, daha yakın zamanlarda karşılaşan ve sadece sonuçtan ziyade öğrenilen derslerin bazılarını paylaşan diğerleri için ekledim.
cdmdotnet

3

Datomic'e bir göz atmak isteyebilirsiniz.

Datomic, esnek ölçeklenebilirlik ve ACID işlemleriyle sorguları ve birleştirmeleri destekleyen esnek, zamana dayalı gerçeklerden oluşan bir veritabanıdır .

Ben detaylı bir cevap yazdı Burada

Sen Datomic tasarımını açıklayan Stuart Halloway bir konuşma izleyebilirsiniz burada

Datomic gerçekleri zamanında sakladığından, olay kaynağı kullanım durumları ve çok daha fazlası için kullanabilirsiniz.


2

Etki alanı modeliniz geliştikçe çözümün (1 ve 2) çok hızlı bir sorun haline gelebileceğini düşünüyorum. Yeni alanlar yaratılır, bazıları anlam değiştirir ve bazıları artık kullanılamaz hale gelir. Sonunda tablonuzda düzinelerce boş değer atanabilir alan olacak ve olayların yüklenmesi karışıklık olacaktır.

Ayrıca, olay deposunun yalnızca yazma işlemleri için kullanılması gerektiğini unutmayın; onu, toplamın özelliklerini değil, yalnızca olayları yüklemek için sorgularsınız. Ayrı şeylerdir (CQRS'nin özü budur).

Çözüm 3 İnsanların genellikle yaptığı gibi, bunu başarmanın birçok yolu vardır.

Örneğin, EventFlow CQRS , SQL Server ile kullanıldığında bu şemayla bir tablo oluşturur:

CREATE TABLE [dbo].[EventFlow](
    [GlobalSequenceNumber] [bigint] IDENTITY(1,1) NOT NULL,
    [BatchId] [uniqueidentifier] NOT NULL,
    [AggregateId] [nvarchar](255) NOT NULL,
    [AggregateName] [nvarchar](255) NOT NULL,
    [Data] [nvarchar](max) NOT NULL,
    [Metadata] [nvarchar](max) NOT NULL,
    [AggregateSequenceNumber] [int] NOT NULL,
 CONSTRAINT [PK_EventFlow] PRIMARY KEY CLUSTERED 
(
    [GlobalSequenceNumber] ASC
)

nerede:

  • GlobalSequenceNumber : Basit global tanımlama, projeksiyonunuzu oluşturduğunuzda (okuma modeli) eksik olayları sıralamak veya tanımlamak için kullanılabilir.
  • BatchId : Atomik olarak eklenen olay grubunun tanımlanması (TBH, bunun neden yararlı olacağına dair hiçbir fikri yok)
  • AggregateId : Toplamın tanımlanması
  • Veri : Serileştirilmiş olay
  • Meta veriler : Olaydan diğer yararlı bilgiler (örn. Seriyi kaldırma için kullanılan olay türü, zaman damgası, komuttan kaynak kimliği, vb.)
  • AggregateSequenceNumber : Aynı toplam içindeki sıra numarası (bu, sıralı olmayan yazmalara sahip değilseniz yararlıdır, bu nedenle bu alanı iyimser eşzamanlılık için kullanırsınız)

Bununla birlikte, sıfırdan yaratıyorsanız, YAGNI prensibini takip etmenizi ve kullanım durumunuz için minimum gerekli alanlarla oluşturmanızı tavsiye ederim.


BatchId'nin potansiyel olarak CorrelationId ve CausationId ile ilişkili olabileceğini iddia ediyorum. Olaylara neyin neden olduğunu bulmak ve gerekirse bunları bir araya getirmek için kullanılır.
Daniel Park

Olabilir. Ancak bu böyledir, onu özelleştirmek için bir yol sağlamak mantıklı olacaktır (örneğin, isteğin kimliği olarak ayarlama), ancak çerçeve bunu yapmaz.
Fabio Marreco

1

Olası ipucu tasarımdır ve ardından "Yavaşça Değişen Boyut" (tür = 2) şunları kapsamanıza yardımcı olacaktır:

  • meydana gelen olayların sırası (vekil anahtar aracılığıyla)
  • her bir durumun dayanıklılığı (- - - - için geçerlidir)

Sol kat işlevinin uygulanması da uygun olmalıdır, ancak gelecekteki sorgu karmaşıklığını düşünmeniz gerekir.


1

Bunun geç bir cevap olacağını tahmin ediyorum, ancak RDBMS'yi olay kaynağı depolama olarak kullanmanın, iş hacmi gereksiniminiz yüksek değilse tamamen mümkün olduğunu belirtmek isterim. Size göstermek için oluşturduğum olay kaynağı hesap defterinin örneklerini göstereceğim.

https://github.com/andrewkkchan/client-ledger-service Yukarıdakiler, bir olay kaynak defteri web hizmetidir. https://github.com/andrewkkchan/client-ledger-core-db Ve yukarıdakileri, işlem desteği gibi bir RDBMS ile gelen tüm avantajlardan yararlanabilmeniz için durumları hesaplamak için RDBMS kullanıyorum. https://github.com/andrewkkchan/client-ledger-core-memory Ve patlamaları işlemek için bellekte işlenecek başka bir tüketicim var.

RDBMS, özellikle ekleme her zaman eklenirken ekleme için yavaş olduğundan, yukarıdaki gerçek olay deposunun hala Kafka'da yaşadığını iddia edebiliriz.

Umarım kod, bu soru için zaten verilmiş olan çok iyi teorik cevapların dışında size bir örnek verir.


Teşekkürler. Uzun zamandır SQL tabanlı bir uygulama oluşturdum. Bir yerde kümelenmiş bir anahtar için verimsiz bir seçim yapmadıysanız, bir RDBMS'nin eklemeler için neden yavaş olduğundan emin değilim. Yalnızca ek iyi olmalıdır.
Neil Barnwell
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.