Apache Kafka voor Data Engineers: Streaming, Partities en Interviewvragen
Apache Kafka voor data engineers: streaming-architectuur, partities, consumer groups, KRaft, CDC met Debezium, exactly-once semantics en interviewvragen met Kafka 4.x voorbeelden.

Apache Kafka heeft zich gevestigd als de ruggengraat van moderne data-architecturen. Van real-time ingestion pipelines tot Change Data Capture en event-driven systemen op schaal -- beheersing van Kafka is onmisbaar voor iedere data engineer. Met de release van Kafka 4.x, dat volledig draait op het KRaft-protocol zonder ZooKeeper, is het ecosysteem operationeel eenvoudiger geworden zonder concessies te doen aan duurzaamheid of ordening. Dit artikel behandelt de kernconcepten, implementatiepatronen en de nieuwste functionaliteiten die regelmatig terugkomen in Data Engineering interviewvragen.
Kafka 4.x KRaft-architectuur, partitioneringsstrategieën, consumer groups met handmatig offsetbeheer, Change Data Capture met Debezium, exactly-once semantics, Share Groups en veelgestelde interviewvragen voor data engineers.
Kafka-architectuur: Brokers, Topics en Partities
Kafka functioneert als een gedistribueerd, onveranderlijk logboek. Producers schrijven records naar topics, elk topic is onderverdeeld in partities en elke partitie wordt gerepliceerd over meerdere brokers. Dit model levert drie cruciale eigenschappen op voor datapipelines: gegarandeerde volgorde binnen een partitie, configureerbare retentie en horizontale schaalbaarheid.
Een productie-cluster bestaat doorgaans uit 3 tot 30 brokers, afhankelijk van het datavolume en de latentie-eisen. Elke broker slaat partitiesegmenten op schijf op en maakt gebruik van de page cache van het besturingssysteem voor extreem snelle sequentiële reads. Dit patroon, bekend als zero-copy, stelt Kafka in staat om throughputs van meer dan 1 GB/s per broker te bereiken op moderne hardware.
De relatie tussen partities en parallellisme is rechtstreeks: het aantal partities van een topic bepaalt het maximale aantal gelijktijdige consumers binnen een consumer group. Bij een topic met 6 partities kunnen maximaal 6 consumers parallel data verwerken. Extra consumers boven dat aantal blijven inactief.
Kafka 4.x en KRaft: het einde van ZooKeeper
Kafka 4.0, uitgebracht in maart 2025, verwijdert de afhankelijkheid van ZooKeeper definitief. Het KRaft-protocol (Kafka Raft) beheert de clustermetadata rechtstreeks binnen de brokers via een op Raft gebaseerd consensusmechanisme. Deze architectuurvereenvoudiging reduceert het aantal te monitoren componenten, versnelt failover en maakt het mogelijk om miljoenen partities per cluster te beheren.
Voor data engineering betekent de eliminatie van ZooKeeper vooral eenvoudigere deployments. Een lokaal ontwikkelcluster kan worden opgestart met een enkel Docker Compose-bestand.
# docker-compose.yml — Kafka 4.x KRaft cluster (no ZooKeeper)
services:
kafka-1:
image: apache/kafka:4.2.0
environment:
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka-1:9093,2@kafka-2:9093,3@kafka-3:9093
KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_LOG_DIRS: /var/lib/kafka/data
KAFKA_NUM_PARTITIONS: 6
KAFKA_DEFAULT_REPLICATION_FACTOR: 3
KAFKA_MIN_INSYNC_REPLICAS: 2
ports:
- "9092:9092"De parameter KAFKA_PROCESS_ROLES: broker,controller geeft aan dat elk knooppunt beide rollen vervult. In productieomgevingen is het gebruikelijk om de controller- en brokerrollen te scheiden op aparte nodes, zodat de metadataverwerking geïsoleerd blijft van de berichtverwerking.
Een replication_factor van 3 met min.insync.replicas van 2 garandeert dat elk bericht dat bevestigd is met acks=all de uitval van een broker overleeft. Deze configuratie biedt een solide balans tussen duurzaamheid en beschikbaarheid voor de meeste datapipelines.
Partitioneringsstrategieën voor Datapipelines
De keuze van de partitiesleutel bepaalt de berichtvolgorde, de verdeling van de werklast en de mogelijkheid om consumers op te schalen. Een verkeerd gekozen sleutel kan leiden tot ongebalanceerde partities (hot partitions) die de belasting concentreren op een enkele consumer terwijl de overige inactief blijven.
Er bestaan drie hoofdstrategieën voor het kiezen van partitiesleutels in data engineering-contexten:
Partitionering per bedrijfsentiteit: Gebruik de identifier van de primaire entiteit (customer_id, account_id) als sleutel. Alle events van dezelfde klant komen in dezelfde partitie terecht, waardoor de chronologische volgorde gegarandeerd is. Dit is de meest gangbare strategie voor pipelines met bestellingen, transacties en gebruikersactiviteit.
Partitionering per sessie: Voor clickstreamdata of telemetrie groepeert de sleutel session_id alle events van een sessie in dezelfde partitie. Het gebruik van user_id zou onjuist zijn als een gebruiker meerdere gelijktijdige sessies kan hebben, omdat de volgorde binnen elke individuele sessie dan verloren gaat.
Round-robin partitionering: Wanneer de volgorde niet relevant is (logs, algemene metrics), worden berichten bij het weglaten van een sleutel gelijkmatig verdeeld. Deze optie maximaliseert het parallellisme ten koste van elke ordeningsgarantie.
# partition_strategy.py — Choosing the right partition key
from confluent_kafka import Producer
import json
def create_producer():
return Producer({
'bootstrap.servers': 'kafka-1:9092',
'acks': 'all',
'enable.idempotence': True,
'max.in.flight.requests.per.connection': 5
})
def publish_order_event(producer, event):
# Key by customer_id: all events for one customer stay ordered
key = str(event['customer_id']).encode('utf-8')
value = json.dumps(event).encode('utf-8')
producer.produce(
topic='order-events',
key=key,
value=value,
headers=[('source', b'order-service'), ('version', b'2')]
)
producer.flush()
def publish_clickstream(producer, event):
# Key by session_id: ordering within a session matters
# NOT by user_id — one user may have multiple sessions
key = str(event['session_id']).encode('utf-8')
value = json.dumps(event).encode('utf-8')
producer.produce(
topic='clickstream',
key=key,
value=value
)De instelling enable.idempotence: True in combinatie met acks: all activeert idempotente productie, waardoor duplicaten worden voorkomen wanneer de producer een verzending opnieuw probeert na een netwerk-timeout. De combinatie met max.in.flight.requests.per.connection: 5 behoudt de volgorde zelfs bij actieve retries -- een verbetering die in Kafka 2.0 werd geïntroduceerd en de eerdere beperking van maximaal 1 in-flight request elimineerde.
Wanneer een partitiesleutel sterk geconcentreerd is (bijvoorbeeld 80% van de events afkomstig van een enkele customer_id), hoopt die partitie een onevenredige belasting op. Het is essentieel om de lag per partitie te monitoren en waar nodig samengestelde sleutels (customer_id + datum) of herpartitionering te overwegen.
Consumer Groups en Handmatig Offsetbeheer
Consumer groups vormen het fundamentele parallellismemechanisme in Kafka. Elke consumer binnen een groep ontvangt een exclusieve subset van partities. Wanneer een consumer uitvalt of een nieuwe wordt toegevoegd, voert Kafka een rebalance uit die de partities herverdeelt over de actieve groepsleden.
Voor datapipelines is het aanbevolen patroon om auto-commit van offsets uit te schakelen en handmatig te bevestigen na de verwerking van elke batch. Deze aanpak garandeert at-least-once semantics: als de consumer uitvalt voor de bevestiging, worden de berichten opnieuw verwerkt bij herstart. De keerzijde is dat de verwerkingslogica idempotent moet zijn om duplicaten correct af te handelen.
Verwerking in micro-batches verbetert de throughput aanzienlijk door de schrijfoperaties naar het warehouse te amortiseren. In plaats van record per record te schrijven, worden berichten gebufferd en wordt er een enkele schrijfoperatie per batch uitgevoerd.
# consumer_pipeline.py — Consumer group with manual offset management
from confluent_kafka import Consumer, KafkaError
import json
def create_consumer(group_id: str):
return Consumer({
'bootstrap.servers': 'kafka-1:9092',
'group.id': group_id,
'auto.offset.reset': 'earliest',
'enable.auto.commit': False, # Manual commit for at-least-once
'max.poll.interval.ms': 300000,
'session.timeout.ms': 45000,
'isolation.level': 'read_committed' # Only read committed transactions
})
def process_batch(consumer: Consumer, batch_size: int = 500):
"""Process records in micro-batches for throughput."""
consumer.subscribe(['order-events'])
buffer = []
while True:
msg = consumer.poll(timeout=1.0)
if msg is None:
continue
if msg.error():
if msg.error().code() == KafkaError._PARTITION_EOF:
continue
raise Exception(msg.error())
record = json.loads(msg.value().decode('utf-8'))
buffer.append(record)
if len(buffer) >= batch_size:
# Process the full batch (write to warehouse, etc.)
write_to_warehouse(buffer)
# Commit AFTER successful processing
consumer.commit(asynchronous=False)
buffer.clear()
def write_to_warehouse(records: list):
# Insert batch into data warehouse (BigQuery, Snowflake, etc.)
passDe parameter isolation.level: read_committed is essentieel wanneer producers transacties gebruiken. Met deze instelling leest de consumer uitsluitend berichten die bevestigd zijn door een succesvolle transactie, waardoor data van afgebroken transacties niet wordt verwerkt.
De configuratie max.poll.interval.ms: 300000 (5 minuten) definieert de maximale tijd tussen aanroepen van poll(). Wanneer de verwerking van een batch langer duurt dan dit interval, beschouwt Kafka de consumer als uitgevallen en start een rebalance. Voor grote batches die naar trage warehouses schrijven, dient deze waarde te worden aangepast.
Klaar om je Data Engineering gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Change Data Capture met Debezium en Kafka Connect
Change Data Capture (CDC) legt wijzigingen op rijniveau in de brondatabase vast en publiceert deze als events in Kafka-topics. Dit patroon elimineert de noodzaak van batch-ETL om operationele databases te synchroniseren met analytische warehouses. Meer over ETL-patronen is te vinden in het artikel over ETL/ELT Pipeline Patterns.
Debezium, de referentie-CDC-connector voor Kafka Connect, leest het replicatielog van PostgreSQL (WAL), MySQL (binlog) of MongoDB (oplog) en converteert elke INSERT, UPDATE en DELETE naar een gestructureerd event. De connector houdt zijn positie in het log bij, waardoor geen enkele wijziging verloren gaat, zelfs niet tijdens herstarts.
{
"name": "postgres-cdc-source",
"config": {
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
"database.hostname": "postgres-primary",
"database.port": "5432",
"database.user": "replication_user",
"database.dbname": "production",
"topic.prefix": "cdc",
"table.include.list": "public.orders,public.customers,public.products",
"slot.name": "debezium_slot",
"plugin.name": "pgoutput",
"publication.name": "dbz_publication",
"snapshot.mode": "initial",
"transforms": "route",
"transforms.route.type": "org.apache.kafka.connect.transforms.RegexRouter",
"transforms.route.regex": "cdc\\.public\\.(.*)",
"transforms.route.replacement": "warehouse.$1"
}
}Het veld snapshot.mode: initial geeft aan dat Debezium eerst een volledige snapshot van de geconfigureerde tabellen maakt en daarna overschakelt naar het streamen van incrementele wijzigingen. De transformatie RegexRouter hernoemt de topics van cdc.public.orders naar warehouse.orders, wat de logische organisatie van topics per bestemming vereenvoudigt.
De replication slot (debezium_slot) garandeert dat PostgreSQL geen WAL-segmenten verwijdert die nog niet zijn geconsumeerd. Het is van cruciaal belang om de grootte van de slot te monitoren: als de connector urenlang stilstaat, kan de slot gigabytes aan WAL ophopen, wat de schijfruimte van de databaseserver beïnvloedt.
Wanneer Debezium stopt of langdurig wordt losgekoppeld, hoopt de replication slot onverwerkte WAL-segmenten op. Het is raadzaam om alerts te configureren op de slotgrootte en een maximale retentielimiet in te stellen in PostgreSQL (max_slot_wal_keep_size).
Exactly-Once Semantics: Transacties in Kafka
Exactly-once semantics garanderen dat elk bericht precies één keer wordt verwerkt, zelfs bij netwerkfouten of herstarts van consumers. Kafka implementeert exactly-once via transacties die de volledige consume-transform-produce cyclus omvatten in een atomaire operatie. Het correct toepassen van dit patroon is een veelvoorkomend onderwerp bij Datamodellering en pipeline-ontwerpvragen.
Het transactionele patroon werkt als volgt: de consumer leest een bericht, transformeert het en publiceert het naar een output-topic. De offset van de consumer en het geproduceerde bericht worden bevestigd binnen dezelfde transactie. Als een van de stappen faalt, wordt de gehele transactie afgebroken en verwerkt de consumer het bericht opnieuw in de volgende cyclus.
# exactly_once_pipeline.py — Transactional consume-transform-produce
from confluent_kafka import Consumer, Producer
import json
def transactional_pipeline():
consumer = Consumer({
'bootstrap.servers': 'kafka-1:9092',
'group.id': 'etl-transformer',
'enable.auto.commit': False,
'isolation.level': 'read_committed'
})
producer = Producer({
'bootstrap.servers': 'kafka-1:9092',
'transactional.id': 'etl-transformer-001',
'acks': 'all',
'enable.idempotence': True
})
producer.init_transactions()
consumer.subscribe(['raw-events'])
while True:
msg = consumer.poll(1.0)
if msg is None or msg.error():
continue
# Begin atomic transaction
producer.begin_transaction()
try:
raw = json.loads(msg.value())
enriched = transform(raw)
# Write transformed record to output topic
producer.produce(
'enriched-events',
key=msg.key(),
value=json.dumps(enriched).encode('utf-8')
)
# Commit consumer offset within the same transaction
producer.send_offsets_to_transaction(
consumer.position(consumer.assignment()),
consumer.consumer_group_metadata()
)
producer.commit_transaction()
except Exception:
producer.abort_transaction()
def transform(record: dict) -> dict:
# Apply business logic transformations
record['processed_at'] = '2026-04-20T00:00:00Z'
record['pipeline_version'] = '2.1'
return recordHet transactional.id identificeert de transactionele producer op unieke wijze. Bij herstart met hetzelfde ID annuleert Kafka automatisch eventuele openstaande transacties van de vorige instantie, waardoor dubbele of verweesde berichten worden voorkomen. Elke pipeline-instantie dient een uniek transactioneel ID te hebben.
Exactly-once introduceert een meetbare latentietoename (tussen 50 en 200 ms extra per transactie). Voor pipelines waar latentie kritiek is en de verwerkingslogica al idempotent is, kunnen at-least-once semantics met handmatige commit geschikter zijn.
Kafka-transacties garanderen exactly-once binnen Kafka (consume-transform-produce). Voor end-to-end exactly-once naar een extern systeem (database, warehouse) is het noodzakelijk om Kafka-transacties te combineren met idempotente schrijfoperaties op de bestemming.
Share Groups: het nieuwe consumptiemodel in Kafka 4.x
Kafka 4.0 introduceert Share Groups, een consumptiemodel waarbij meerdere consumers berichten van dezelfde partitie gelijktijdig kunnen verwerken. In tegenstelling tot klassieke consumer groups, waar elke partitie exclusief aan een consumer wordt toegewezen, verdelen Share Groups individuele berichten over de groepsleden.
Dit model is bijzonder geschikt voor workloads waar de volgorde niet kritiek is maar parallellisme wel (verwerking van notificaties, onafhankelijke validaties, stateless verrijkingstaken). Share Groups maken het mogelijk om consumers op te schalen voorbij het aantal partities, waardoor een historische beperking van Kafka verdwijnt.
| Feature | Consumer Groups | Share Groups | |---|---|---| | Partition assignment | Exclusive (one consumer per partition) | Shared (multiple consumers per partition) | | Ordering guarantee | Per-partition ordering preserved | No ordering guarantee | | Use case | Ordered event streams | Task distribution, work queues | | Acknowledgement | Offset-based (commit position) | Per-record (acknowledge individually) | | Max parallelism | Limited by partition count | Limited by consumer count |
Share Groups vervangen de traditionele consumer groups niet. Het betreft een complementair mechanisme voor scenario's waarin Kafka wordt gebruikt als werkwachtrij in plaats van als geordend logboek. De keuze tussen beide modellen hangt af van de vraag of de berichtvolgorde een vereiste is van de pipeline.
Veelgestelde Interviewvragen over Kafka voor Data Engineers
Technische sollicitatiegesprekken voor data engineering-posities bevatten regelmatig vragen over Kafka. De volgende vragen dekken de meest voorkomende onderwerpen en de verwachte antwoorden.
Hoe garandeert Kafka de volgorde van berichten? Kafka garandeert de volgorde uitsluitend binnen een partitie. Om de volgorde van gerelateerde events te behouden, dient een partitiesleutel te worden gebruikt die deze events in dezelfde partitie groepeert (bijvoorbeeld customer_id voor klantevents).
Wat is het verschil tussen at-least-once en exactly-once? Bij at-least-once kan een bericht meer dan eens worden verwerkt als de consumer uitvalt na verwerking maar voor de offsetbevestiging. Exactly-once maakt gebruik van transacties om de offsetbevestiging van de consumer en de productie van het getransformeerde bericht atomair te bevestigen.
Hoe werkt het rebalancen van consumer groups? Wanneer een consumer toetreedt tot of een groep verlaat, herverdeelt Kafka de partities over de actieve leden. Tijdens de rebalance stopt de verwerking. Het Cooperative Sticky Assignor-protocol minimaliseert de onderbreking door alleen de strikt noodzakelijke partities opnieuw toe te wijzen.
Wat gebeurt er als een consumer te lang nodig heeft voor de verwerking?
Als de tijd tussen aanroepen van poll() het max.poll.interval.ms overschrijdt, beschouwt Kafka de consumer als uitgevallen en start een rebalance. De oplossing is het verkleinen van de batchgrootte, het verhogen van het interval of het verwerken in een aparte thread.
Waarom heeft Kafka 4.x ZooKeeper verwijderd? ZooKeeper bracht extra operationele complexiteit met zich mee, beperkte de schaalbaarheid van metadata en vereiste een apart cluster met eigen beheer. KRaft integreert het metadatabeheer rechtstreeks in de Kafka-brokers, wat deployments vereenvoudigt en failover versnelt.
Wanneer zijn Share Groups geschikter dan consumer groups? Share Groups zijn geschikt wanneer berichtvolgorde niet vereist is en consumers voorbij het aantal partities moeten worden opgeschaald. Ze zijn ideaal voor workloads van het type werkwachtrij (job queue), zoals het verwerken van notificaties of onafhankelijke validaties.
Hoe worden hot partitions voorkomen? Door de lag per partitie en de sleutelverdeling te monitoren. Als een sleutel te veel berichten concentreert, kan een samengestelde sleutel worden gebruikt, een willekeurig achtervoegsel worden toegevoegd (ten koste van de volgorde) of het topic worden herpartitioneerd met meer partities.
Conclusie
Apache Kafka is het centrale onderdeel van moderne real-time data-architecturen. De belangrijkste punten voor het beheersen van Kafka als data engineer zijn:
- Kafka 4.x verwijdert ZooKeeper ten gunste van KRaft, wat deployments vereenvoudigt en het metadatabeheer opschaalt naar miljoenen partities
- De keuze van de partitiesleutel bepaalt de volgorde, het parallellisme en de lastverdeling -- customer_id voor bestelpipelines en session_id voor clickstream
- Consumer groups met handmatige offsetcommit bieden at-least-once semantics, het meest gangbare patroon voor ingestion pipelines naar warehouses
- CDC met Debezium vangt wijzigingen op rijniveau op vanuit PostgreSQL, MySQL of MongoDB zonder de operationele database te belasten met extractiequery's
- Kafka-transacties maken exactly-once mogelijk binnen het Kafka-ecosysteem door consume-transform-produce in een atomaire operatie te verpakken
- Share Groups in Kafka 4.x stellen consumers in staat om op te schalen voorbij het aantal partities voor workloads waar de volgorde niet kritiek is
- In sollicitatiegesprekken wordt kennis verwacht van partitionering, leveringssemantieken, rebalancing van consumer groups en de verschillen tussen KRaft en ZooKeeper
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Tags
Delen
Gerelateerde artikelen

De 25 belangrijkste Data Engineering sollicitatievragen in 2026
Een praktische gids met de 25 meest gestelde sollicitatievragen voor data engineers in 2026. Van SQL-optimalisatie en pipeline-architectuur tot system design en datakwaliteit, met codevoorbeelden en uitgebreide antwoorden.

ETL vs ELT in 2026: Architectuur van datapipelines uitgelegd
ETL vs ELT vergelijking voor moderne datapipelines. Architectuurverschillen, prestatie-afwegingen en wanneer welke aanpak te gebruiken met Snowflake, BigQuery en dbt in 2026.

Apache Spark met Python: datapijplijnen stap voor stap bouwen
Een praktische PySpark-tutorial over DataFrame-operaties, de opbouw van ETL-pijplijnen en de functies van Spark 4.0. Met productie-klare codevoorbeelden voor data engineers die zich voorbereiden op technische interviews.