İçeriğe atla

Değişiklik Verisi Yakalama Mimarisi: Debezium ve Kafka ile Log Tabanlı CDC

Log tabanlı değişiklik verisi yakalama uçtan uca: veritabanı logları, Kafka Connect üzerinde Debezium, snapshot'lar, sıralama, şema değişiklikleri ve slot tuzağı.

· Kurucu ve geliştirici

Yayımlandı:

Sipariş servisi Postgres'e yazar. Kafka Connect çerçevesindeki Debezium bağlayıcısı WAL'ı bir replikasyon slotu üzerinden okur ve değişiklik olaylarını bir Kafka topic'ine yayımlar; şemalar bir registry'de durur. Arama indeksi, önbellek, veri ambarı ve okuma modeli servisi topic'i tüketir.

Orders tablosu Postgres'te duruyor ve şimdi beş başka yerin daha ona ihtiyacı var: arama indeksi, bir önbellek, veri ambarı, bir dolandırıcılık servisi ve bir raporlama okuma modeli. Hepsine uygulamadan yazmak er ya da geç yarıda başarısız olur. Tabloyu dakikada bir sorgulamak silmeleri kaçırır ve yük ekler. Log tabanlı değişiklik verisi yakalama (CDC) farklı bir yol izler: Veritabanının kendi işlem logunu, yani veritabanının çökme kurtarma ve replikasyon için kullandığı kaydın aynısını okur ve onaylanan her değişikliği bir olaya dönüştürür.

Bu yazı Debezium ve Kafka ile log tabanlı CDC'yi bir mimari diyagramla açıklıyor: yaklaşımların karşılaştırması, boru hattının nasıl çalıştığı, bir değişiklik olayının neler içerdiği, snapshot'lar, sıralama ve teslim garantileri, şema değişiklikleri, birincil sunucuları çökerten operasyonel tuzak ve bunun yerine ne zaman outbox desenini kullanmanız gerektiği.

Değişiklikleri yakalamanın dört yolu

Yaklaşım Nasıl çalışır Asıl sorun
Çift yazma Uygulama hem veritabanına hem bir broker'a yazar Yazmalardan biri başarısız olurken diğeri başarılı olabilir ve bunları hiçbir şey uzlaştırmaz
Sorgulama (polling) Bir iş, son çalışmadan daha yeni updated_at değerine sahip satırları sorgular Silmeleri ve ara durumları kaçırır; sorgu yükü ekler
Tetikleyiciler Veritabanı tetikleyicileri değişiklikleri bir denetim tablosuna kopyalar Her yazmaya maliyet ekler; mantık veritabanında gizli kalır
Log tabanlı Bir bağlayıcı işlem logunu okur Veritabanı yapılandırması ve dikkatli işletim gerektirir

Log tabanlı CDC, silmeler dahil onaylanan her değişikliği, uygulamaya dokunmadan ve yazmaları yavaşlatmadan, onaylanma sırasıyla gören tek yaklaşımdır. Yazının geri kalanı bununla ilgili.

Log tabanlı CDC nasıl çalışır

Veritabanları her değişikliği uygulamadan önce zaten bir loga yazar: Postgres'te write-ahead log, MySQL'de binary log, SQL Server'da işlem logu, MongoDB change stream'lerinin arkasında oplog. Bir CDC bağlayıcısı bu logu bir replikanın okuyacağı şekilde okur.

Postgres için bu, mantıksal kod çözme (logical decoding) demektir:

-- postgresql.conf: wal_level = logical (requires a restart)
CREATE PUBLICATION orders_pub FOR TABLE orders, order_items;

-- Include all old column values in update and delete events
ALTER TABLE orders REPLICA IDENTITY FULL;

Bağlayıcı, ne kadar okuduğunu hatırlayan bir replikasyon slotu oluşturur ve değişiklikleri Postgres'e yerleşik pgoutput eklentisiyle çözer. Varsayılan olarak güncelleme ve silme olayları yalnızca eski birincil anahtarı taşır; REPLICA IDENTITY FULL daha büyük bir log pahasına eski satırın tamamını ekler. MySQL'de bunun karşılığı, güncel sürümlerde varsayılan olan, tam satır görüntülü satır tabanlı binary log'dur.

Kafka Connect üzerinde Debezium

Debezium yaygın kullanılan açık kaynaklı bir CDC motorudur. Bağlayıcıları Kafka Connect içinde çalışır, logu okur ve satır değişikliği başına bir olayı, tablo başına bir Kafka topic'ine, satırın birincil anahtarıyla anahtarlanmış olarak yayımlar. Minimal bir Postgres bağlayıcı yapılandırması:

{
  "name": "orders-cdc",
  "config": {
    "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
    "plugin.name": "pgoutput",
    "database.hostname": "orders-db",
    "database.port": "5432",
    "database.user": "debezium",
    "database.dbname": "orders",
    "topic.prefix": "shop",
    "table.include.list": "public.orders,public.order_items",
    "publication.name": "orders_pub",
    "slot.name": "orders_cdc",
    "heartbeat.interval.ms": "10000"
  }
}

public.orders tablosundaki değişiklikler shop.public.orders topic'ine düşer. Parolayı bir secrets sağlayıcısıyla dosyanın dışında tutun. Kafka çalıştırmıyorsanız Debezium Server aynı olayları başka broker'lara ve bulut kuyruklarına akıtır.

Bir değişiklik olayının anatomisi

Yalnızca payload'una indirildiğinde bir güncelleme olayı şöyle görünür:

{
  "before": { "id": 1042, "status": "pending", "total": 59.90 },
  "after":  { "id": 1042, "status": "paid",    "total": 59.90 },
  "source": { "db": "orders", "table": "orders", "lsn": 24023128, "txId": 5521 },
  "op": "u",
  "ts_ms": 1793606400123
}

op alanı oluşturma için c, güncelleme için u, silme için d ve snapshot sırasında okunan satırlar için r değerini alır. Bir silmenin ardından bir tombstone gelir: aynı anahtarlı, değeri null olan bir mesaj. Böylece Kafka log compaction anahtarı sonunda tamamen kaldırabilir. source bloğu değişikliğin logda tam olarak nereden geldiğini kaydeder ve aşağı akışta tekilleştirmeyi mümkün kılan da budur.

Snapshot'lar: zaten var olan satırlar

Yeni bir bağlayıcı, milyonlarca satırı olan bir tabloyla başlar ve log yalnızca son değişiklikleri içerir. Bu yüzden bağlayıcı önce bir ilk snapshot alır, var olan her satırı bir r olayı olarak yayar, ardından snapshot başladığında kaydettiği log konumundan itibaren akışa geçer; böylece arada hiçbir şey kaybolmaz.

Büyük tablolarda uzun, engelleyici bir snapshot zahmetlidir. Artımlı snapshot'lar, Debezium ekibinin açıkladığı gibi Netflix'in DBLog makalesindeki watermark tekniğini kullanarak akış devam ederken tabloyu parçalar hâlinde kopyalar. Bunları bir sinyal tablosu üzerinden tetiklersiniz ve yeniden başlatmadan sonra baştan başlamak yerine kaldıkları yerden devam ederler.

Sıralama ve teslim garantileri

  • Satır bazında olaylar sırasını korur. Bir birincil anahtara ait bütün olaylar aynı anahtarı paylaşır, dolayısıyla aynı Kafka partition'ına düşer.
  • Satırlar ve tablolar arasında küresel bir sıra yoktur. Tek bir işlemde değişen iki tablo, iki topic'te iki olay hâline gelir. İşlem sınırlarına ihtiyacınız varsa Debezium, tüketicilerin olayları yeniden gruplamasını sağlayan işlem meta veri olayları yayabilir.
  • Teslim en az bir kezdir. Bir çökme ya da yeniden başlatmadan sonra bağlayıcı bazı olayları yeniden gönderebilir. Debezium belgeleri bunu açıkça söyler.

Tüketiciler için sonucu şu: Onları idempotent yapın. Anahtara göre upsert yapın ve log konumu, o anahtar için en son uygulanandan yeni olmayan olayları yok sayın.

Şema değişiklikleri

Bir tablo yakalanmaya başladığında şeması her tüketiciyle bir sözleşmeye dönüşür. Null olabilen bir sütun eklemek güvenlidir; bir sütunu yeniden adlandırmak ya da tipini değiştirmek birilerini bozar. Avro, Protobuf ya da JSON Schema ile bir schema registry kullanın ve uyumluluk kuralları uygulayın; böylece kırıcı bir değişiklik üretimde değil, CI'da başarısız olur. Daha da iyisi, hangi tabloların herkese açık olduğuna karar verin. Servis dışında hiçbir şey onları tüketmiyorsa iç tablolar özgürce değişebilir.

Replikasyon slotu tuzağı

Bu, çoğu ekibin zor yoldan öğrendiği operasyonel derstir. Bir Postgres replikasyon slotu, bağlayıcının onaylamadığı her WAL segmentini tutar. Bağlayıcı durursa ya da çok geride kalırsa WAL, disk dolana ve veritabanı yazma kabul etmeyi bırakana kadar birincil sunucuda birikir. max_slot_wal_keep_size için Postgres varsayılanı -1'dir; yani slotlar sınırsız miktarda WAL tutabilir.

Aynı sorunun daha sessiz bir versiyonu da var: Yakalanan tablolar boşta dururken diğer tablolar yoğundur. Bağlayıcının onaylayacak bir şeyi olmadığı için slot hiç ilerlemez. Debezium'un heartbeat ayarı tam olarak bu durum için vardır.

Savunmalar:

-- How much WAL each slot is holding back
SELECT slot_name, active,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS retained
FROM pg_replication_slots;
  • Yalnızca disk kullanımına değil, slot başına tutulan WAL'a da alarm kurun.
  • max_slot_wal_keep_size değerini diskin kaldırabileceği bir boyuta ayarlayın. Bunu aşan bir slot geçersiz kılınır ve bağlayıcının yeniden snapshot alması gerekir; bu, dolu bir diskten çok daha iyidir.
  • Heartbeat'leri etkinleştirin ve heartbeat tablosunu publication'a ekleyin.
  • Bir bağlayıcıyı devreden çıkarırken slotunu silin.
  • Failover'ı planlayın. PostgreSQL 17 mantıksal slotları fiziksel bir standby'a senkronize edebilir; kurulumunuzun ve bağlayıcı sürümünüzün bunu kullandığını kontrol edin.

CDC mi, outbox deseni mi?

Tabloları doğrudan yakalamak iç şemanızı olaylar olarak dışarı açar. Alternatifi outbox desenidir: Servis, verisiyle aynı işlem içinde açık olayları bir outbox tablosuna yazar ve CDC yalnızca o tabloyu yayımlar. Debezium bunun için bir outbox event router içerir.

Tablo CDC'si CDC ile outbox
Olay biçimi Tablonun şemasında satır değişiklikleri Tüketiciler için tasarlanmış olaylar
Uygulama değişiklikleri Yok Her işlemde outbox'a yazma
Bağımlılık Tüketiciler iç tablolara bağımlıdır Tüketiciler yayımlanmış bir sözleşmeye bağımlıdır
En uygun olduğu iş Replikasyon, arama, önbellekler, analitik Servisler arası entegrasyon olayları

Transactional outbox deseni yazısı ikinci sütunu ayrıntılı olarak ele alıyor.

CDC boru hattı diyagramı

Sipariş servisi Postgres'e yazar. Kafka Connect çerçevesindeki Debezium bağlayıcısı WAL'ı bir replikasyon slotu üzerinden okur ve değişiklik olaylarını bir Kafka topic'ine yayımlar; şemalar bir registry'de durur. Arama indeksi, önbellek, veri ambarı ve okuma modeli servisi topic'i tüketir.

11 bileşen · 9 bağlantı

Archboard'da aç
Diyagram içeriği
  • Orders service
  • Postgres: WAL + slot orders_cdc
  • Kafka Connect
  • Debezium connector
  • shop.public.orders, key: id
  • Schema registry
  • Search index
  • Cache
  • Lakehouse bronze
  • Read-model service
  • INSERT / UPDATE / DELETE
  • WAL changes
  • confirm LSN
  • change events
  • schemas
  • at least once
Log tabanlı değişiklik verisi yakalama. Sipariş veritabanı her değişikliği loguna yazar; Debezium logu bir replikasyon slotu üzerinden okur ve satır değişikliği başına bir olayı Kafka'ya yayımlar. Orada bir arama indeksi, bir önbellek, lakehouse ve bir okuma modeli bu olayları tüketir.

Nasıl okunur:

  • Sipariş servisi her zamanki gibi Postgres'e yazar; CDC hakkında hiçbir şey bilmez.
  • Kafka Connect çerçevesindeki Debezium, write-ahead log'u replikasyon slotu üzerinden okur, değişiklik olaylarını shop.public.orders topic'ine yayımlar ve şemaları registry'ye kaydeder.
  • Dört tüketici topic'i birbirinden bağımsız okur: arama indeksi, Redis önbelleği, veri ambarı ve bir okuma modeli servisi.
  • Postgres'e geri dönen kesikli kenar, slot onayıdır; WAL'ın birikmesini önleyen bağlantı budur.
  • Markdown bloğu kısaltılmış bir değişiklik olayını ve slot gecikme sorgusunu gösterir.

CDC boru hatlarını çizmek

  • Yalnızca veritabanını değil, logu da çizin. CDC'nin başarılı olduğu ya da başarısız olduğu yer slot ve WAL'dır.
  • Topic adlarını ve anahtarları gösterin. “Sipariş ID'siyle anahtarlanmış” ifadesi sıralamayı birkaç kelimeyle açıklar.
  • Her tüketiciyi ayrı çizin. Her birinin kendi gecikmesi, arıza biçimi ve idempotency mantığı vardır.
  • Teslim garantisini diyagrama yazın: en az bir kez.

Kafka tarafı Kafka mimari diyagramında ele alınıyor. CDC, veri boru hattı mimari diyagramında gösterilen bir lakehouse'un ham katmanını beslemenin ve CQRS ve event sourcing diyagramı için okuma modelleri oluşturmanın olağan yoludur.

Archboard'da oluşturun

  1. Google ya da GitHub ile giriş yapın ve bir diyagram oluşturun.
  2. Sipariş servisi için Bulut paketinden bir Hesaplama düğümü ve Veri paketinden bir Postgres düğümü ekleyin.
  3. Kafka Connect için, içinde Debezium bağlayıcısını temsil eden bir Hesaplama düğümü olan bir Temel Çerçeve ekleyin.
  4. Topic için Veri paketinden bir Kafka düğümü, schema registry için de bir Temel Kutu ekleyin.
  5. Tüketicileri ekleyin: arama indeksi için Bulut paketinden bir Yönetilen veritabanı, bir Redis düğümü, bir Veri ambarı düğümü ve okuma modeli servisi için bir Hesaplama düğümü.
  6. Slot onayları için bağlayıcıdan Postgres'e kesikli bir kenar çizin, topic'i anahtarıyla etiketleyin ve olay örneğiyle Temel paketinden bir Markdown bloğu ekleyin.

Örnek 11 diyagram düğümü kullanıyor ve Free planın sınırları içinde kalıyor.

SSS

Log tabanlı değişiklik verisi yakalama nedir? Postgres write-ahead log'u ya da MySQL binary log'u gibi bir veritabanı işlem logunu okuyup onaylanan her değişikliği bir olaya dönüştürmek. Uygulamayı değiştirmeden ekleme, güncelleme ve silmeleri sırasıyla yakalar.

Debezium tam olarak bir kez teslim garantisi verir mi? Hayır. Normal çalışma sırasında her değişiklik bir kez teslim edilir, ancak arızalardan sonra bazı olaylar yeniden teslim edilebilir; bu yüzden Debezium en az bir kez teslimi belgeler. Tüketiciler idempotent olmalıdır.

CDC kurduktan sonra Postgres diskim neden doldu? Bir replikasyon slotu, bağlayıcı onaylayana kadar WAL'ı tutar. Duran bir bağlayıcı ya da diğerleri değişirken boşta kalan yakalanmış tablolar WAL'ın birikmesine yol açar. Slot gecikmesini izleyin, heartbeat'leri etkinleştirin ve max_slot_wal_keep_size değerini ayarlayın.

CDC var olan veriyi nasıl ele alır? Bir snapshot ile. Bağlayıcı önce var olan her satırı yayar, ardından başlangıçta kaydettiği log konumundan değişiklikleri akıtır. Artımlı snapshot'lar bunu, akış devam ederken parçalar hâlinde yapar.

CDC mi kullanmalıyım, outbox deseni mi? Tablo CDC'si replikasyona, aramaya, önbelleklere ve analitiğe uyar. Servisler arasındaki olaylar için outbox deseni, iç tablo satırları yerine tasarlanmış bir sözleşme yayımlar.

Ücretsiz başlayın — tek kullanıcı, kredi kartı gerekmez.

İlgili yazılar

← Blog'a dön

Bir dakikada canvas açın

Google veya GitHub ile giriş yapın. Tek koltuk ücretsiz; ekip katılınca planı yükseltin.