# Apache Kafka w Data Engineering: Partycje, Consumer Groups i Pipeline'y Strumieniowe > Kompleksowy przewodnik po Apache Kafka dla inzynierow danych. Architektura streamingu w trybie KRaft, strategie partycjonowania, consumer groups, Kafka Connect z Debezium, semantyka exactly-once, Share Groups (KIP-932) oraz pytania rekrutacyjne z przykladami kodu Python. - Published: 2026-04-20 - Updated: 2026-04-20 - Author: SharpSkill - Tags: kafka, data-engineering, streaming, partitions - Reading time: 12 min --- Apache Kafka stanowi rdzen wiekszosci nowoczesnych stosow technologicznych w data engineeringu, obslugujac biliony zdarzen dziennie w organizacjach kazdej wielkosci. Wraz z wydaniem Kafka 4.x, platforma dziala calkowicie w oparciu o KRaft (bez zaleznosci od ZooKeepera), co znaczaco uproscilo operacje administracyjne przy zachowaniu tych samych gwarancji niezawodnosci, ktore uczynialy z niej standard branzy w dziedzinie pipeline'ow przetwarzania danych w czasie rzeczywistym. Niniejszy artykul omawia kluczowe aspekty Apache Kafka z perspektywy inzyniera danych -- od architektury partycji, przez zarzadzanie offsetami, po transakcyjne pipeline'y exactly-once -- z produkcyjnymi przykladami kodu i pytaniami przygotowujacymi do [rozmow rekrutacyjnych z zakresu data engineeringu](/pl/technologies/data-engineering). > **Kafka 4.x i tryb KRaft** > > Od wersji Apache Kafka 4.0 ZooKeeper zostal calkowicie wyeliminowany z architektury. Tryb KRaft (Kafka Raft) obsluguje metadane klastra natywnie, co redukuje liczbe komponentow do wdrozenia i administracji. Kazdy nowy klaster Kafka powinien byc uruchamiany wylacznie w trybie KRaft. ## Architektura strumieniowa Kafka dla pipeline'ow danych Podstawowym wymaganiem przy projektowaniu pipeline'ow strumieniowych opartych na Kafce jest prawidlowe skonfigurowanie klastra. Od wersji 4.0 ZooKeeper nie jest juz potrzebny -- tryb KRaft integruje mechanizm konsensusu bezposrednio w brokerach, eliminujac zewnetrzna zaleznosc i redukujac zlozonosc operacyjna. Ponizszy plik Docker Compose definiuje klaster Kafka 4.2 zlozony z trzech wezlow w trybie KRaft. Kazdy wezel pelni jednoczesnie role brokera i kontrolera, co jest odpowiednia konfiguracja dla srodowisk deweloperskich i testowych: ```yaml # 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" ``` Zmienna `KAFKA_CONTROLLER_QUORUM_VOTERS` okresla wezly uczestniczace w kworum Raft. Wspolczynnik replikacji ustawiony na 3 w polaczeniu z minimum 2 zsynchronizowanymi replikami (`MIN_INSYNC_REPLICAS`) oznacza, ze klaster toleruje utrate jednego wezla bez przerwy w dzialaniu i bez utraty danych. Szesc domyslnych partycji (`KAFKA_NUM_PARTITIONS: 6`) pozwala na zrownoleglenie przetwarzania po stronie konsumentow juz na etapie konfiguracji klastra. W srodowiskach produkcyjnych zaleca sie separacje rol kontrolera i brokera na dedykowane maszyny, aby odizolowac zarzadzanie metadanymi od obslugi ruchu klientow. ## Strategie partycjonowania wplywajace na wydajnosc pipeline'ow Partycje sa fundamentalnym mechanizmem rownoleglego przetwarzania w Kafce. Kazdy topic jest dzielony na partycje, z ktorych kazda stanowi uporzadkowany, niemutowalny log. Wiadomosci w obrebie pojedynczej partycji zachowuja scisly porzadek sekwencyjny, natomiast miedzy partycjami nie istnieje zadna gwarancja kolejnosci globalnej. Wybor klucza partycjonowania to jedna z najwazniejszych decyzji projektowych. Klucz determinuje, ktore wiadomosci trafiaja do ktorej partycji, co bezposrednio wplywa na kolejnosc przetwarzania, rozklad obciazenia i mozliwosci skalowania systemu. ```python # 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 ) ``` W powyzszym przykladzie zdarzenia zamowien sa partycjonowane po `customer_id`. Wszystkie zdarzenia dotyczace tego samego klienta trafiaja do jednej partycji, co gwarantuje ich przetwarzanie w kolejnosci chronologicznej. W przypadku danych clickstream klucz partycjonowania oparty jest na `session_id` (a nie `user_id`), poniewaz jeden uzytkownik moze prowadzic wiele sesji jednoczesnie. Dzieki temu porzadek zdarzen w ramach sesji zostaje zachowany, a obciazenie rozklada sie rownomiernie na wieksza liczbe partycji. Konfiguracja `enable.idempotence: True` razem z `acks: all` aktywuje idempotentna produkcje, eliminujac duplikaty powstajace podczas ponownych prob przesylania po bledach sieciowych. Parametr `max.in.flight.requests.per.connection: 5` umozliwia do pieciu rownoleglych zapytan z zachowaniem kolejnosci -- funkcjonalnosc dostepna od Kafka 2.0. > **Uwaga na hot partitions** > > Zle dobrany klucz partycjonowania prowadzi do zjawiska hot partitions. Jesli np. 80% ruchu pochodzi od jednego klienta, a jego identyfikator sluzy jako klucz, jedna partycja przejmie wiekszosc wiadomosci i przeciazy odpowiedzialnego brokera. Zasada jest prosta: klucz partycjonowania powinien generowac rownomierny rozklad wartosci. W razie potrzeby mozna stosowac klucze zlozone (np. customer_id + data) lub niestandardowe partitionery. ## Consumer Groups i zarzadzanie offsetami Consumer groups umozliwiaja wielu instancjom jednego serwisu wspolne przetwarzanie topica. Kafka automatycznie przypisuje kazda partycje do dokladnie jednego konsumenta w grupie, co gwarantuje brak podwojnego przetwarzania w obrebie grupy. Liczba partycji topica wyznacza gorny limit rownoleglego przetwarzania -- jesli topic posiada 12 partycji, trzynasty konsument w grupie pozostanie bezczynny. Kluczowym aspektem jest zarzadzanie offsetami, czyli pozycjami odczytu w poszczegolnych partycjach. Tryb domyslny (`enable.auto.commit = True`) zatwierdza offsety periodycznie, co w przypadku awarii moze powodowac podwojne przetwarzanie lub utrate wiadomosci. Dla pipeline'ow danych reczne zatwierdzanie offsetow po zakonczeniu przetwarzania stanowi wymog konieczny. ```python # 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.) pass ``` Parametr `isolation.level: read_committed` blokuje odczyt wiadomosci z przerwanych transakcji. W polaczeniu z recznym synchronicznym zatwierdzaniem wzorzec ten gwarantuje semantyke at-least-once: kazda wiadomosc zostanie przetworzona co najmniej raz, nawet po restarcie konsumenta. Rozmiar mikro-paczki (500 rekordow) to kompromis miedzy latencja a przepustowoscia -- wieksza paczka zwieksza throughput, ale wydluza czas przetwarzania. Parametr `max.poll.interval.ms: 300000` (5 minut) definiuje maksymalny dopuszczalny odstep miedzy kolejnymi wywolaniami `poll()`. Przekroczenie tego limitu powoduje, ze Kafka uznaje konsumenta za martwego i uruchamia rebalancing grupy. Przy wolnych operacjach zapisu do hurtowni danych (np. BigQuery, Snowflake) wartosc ta wymaga starannego dostosowania. ## Kafka Connect i integracja pipeline'ow danych Change Data Capture (CDC) to jeden z najbardziej wartosciowych scenariuszy uzycia Kafki w kontekscie data engineeringu. Zamiast okresowego odpytywania bazy danych (polling), Debezium odczytuje dziennik transakcji (WAL w PostgreSQL, binlog w MySQL) i publikuje kazda zmiane jako zdarzenie w topicu Kafka. Podejscie to oferuje niemal zerowe opoznienie propagacji, brak dodatkowego obciazenia bazy zrodlowej oraz kompletne przechwytywanie wszystkich operacji, wlacznie z usuwaniem rekordow. ```json { "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" } } ``` Powysza konfiguracja Debezium przechwytuje zmiany w tabelach `orders`, `customers` i `products` produkcyjnej bazy PostgreSQL. Parametr `snapshot.mode: initial` powoduje wykonanie pelnej migawki aktualnego stanu tabel przed przejsciem w tryb strumieniowy przechwytywania zmian przyrostowych. Transformacja `RegexRouter` zmienia nazwy topicow wyjsciowych -- na przyklad `cdc.public.orders` staje sie `warehouse.orders` -- co upraszcza routing danych do hurtowni. Slot replikacji (`debezium_slot`) gwarantuje, ze PostgreSQL nie usunie segmentow WAL, ktore nie zostaly jeszcze skonsumowane przez konektor. Monitorowanie rozmiaru slotu jest kluczowe: dluzsze przestoje konektora moga spowodowac zgromadzenie gigabajtow danych WAL i zagrozic pojemnosci dyskowej serwera bazodanowego. ## Semantyka exactly-once w pipeline'ach Kafka Semantyka exactly-once stanowi najbardziej wymagajacy model gwarancji dostarczania w systemach strumieniowych. Bez niej pipeline naraza sie na generowanie duplikatow (at-least-once) lub utrate wiadomosci (at-most-once). Kafka realizuje exactly-once poprzez transakcje producenta w polaczeniu z izolacja `read_committed` po stronie konsumenta. Wzorzec consume-transform-produce stanowi modelowy przyklad tego mechanizmu: konsument odczytuje wiadomosc ze zrodlowego topica, przeksztalca ja, produkuje wynik do topica docelowego i zatwierdza offset wiadomosci zrodlowej -- wszystko w obrebie jednej atomowej transakcji. ```python # 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 record ``` Parametr `transactional.id` jednoznacznie identyfikuje instancje pipeline'u. W przypadku restartu Kafka wykorzystuje ten identyfikator do anulowania wszelkich niedokonczonych transakcji poprzedniej instancji, zapobiegajac tym samym duplikatom. Wywolanie `send_offsets_to_transaction` laczy zatwierdzenie offsetu konsumenta z ta sama transakcja co produkcja wzbogaconej wiadomosci -- albo obie operacje zostana zatwierdzone razem, albo zadna z nich nie wejdzie w zycie. Nalezy pamietac, ze transakcje Kafka wprowadzaja dodatkowy narzut operacyjny. Kazda transakcja generuje zapisy do wewnetrznych topicow koordynacyjnych. Dla pipeline'ow o bardzo wysokiej przepustowosci (ponad 100 000 wiadomosci na sekunde) rekomendowane jest grupowanie wielu wiadomosci w jednej transakcji -- optymalnie od 100 do 500 rekordow na transakcje -- co zapewnia najlepszy kompromis miedzy gwarancjami dostarczania a wydajnoscia systemu. ## Share Groups: semantyka kolejek w Kafka 4.2 Kafka 4.0 wprowadza Share Groups (KIP-932) -- fundamentalna zmiane w modelu konsumpcji. W klasycznych consumer groups kazda partycja jest przypisana wylacznie do jednego konsumenta. Share Groups zrywaja z tym ograniczeniem, pozwalajac wielu konsumentom na jednoczesny odczyt tej samej partycji z mechanizmem potwierdzania na poziomie pojedynczej wiadomosci. Model ten czerpie inspiracje z tradycyjnych kolejek komunikatow (RabbitMQ, SQS), zachowujac jednoczesnie zalety infrastruktury Kafka. Jest szczegolnie przydatny w scenariuszach, gdzie kolejnosc przetwarzania nie jest krytyczna, ale rownomierne rozlozenie obciazenia miedzy konsumentami ma kluczowe znaczenie. | 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 nie zastepuja klasycznych consumer groups -- oba modele wspolistnieja i odpowiadaja na odmienne potrzeby. Dla pipeline'ow ETL, gdzie kolejnosc zdarzen jest kluczowa, klasyczne consumer groups pozostaja wlasciwym wyborem. Do dystrybucji zadan obliczeniowych -- generowania raportow, wysylki powiadomien, inferencji modeli ML -- Share Groups oferuja lepsza skalowalnosc, poniewaz stopien rownoleglego przetwarzania nie jest ograniczony liczba partycji, lecz liczba konsumentow. Warto rowniez zwrocic uwage na mechanizm ponownego dostarczania (redelivery) na poziomie pojedynczej wiadomosci. Jesli konsument nie potwierdzi przetworzenia w okreslonym czasie, Kafka automatycznie przekazuje wiadomosc innemu konsumentowi w grupie. Zachowanie to jest analogiczne do visibility timeout w Amazon SQS i stanowi istotna roznice wzgledem offsetowego modelu klasycznych consumer groups. > **Share Groups a istniejace pipeline'y** > > Share Groups stanowia uzupelnienie, a nie zastepstwo dla klasycznych consumer groups. Przy migracji istniejacych pipeline'ow nie nalezy automatycznie przechodzic na Share Groups. Nowy model sprawdza sie przede wszystkim w scenariuszach typu kolejka robocza -- przetwarzanie obrazow, wysylka powiadomien, generowanie raportow -- gdzie kolejnosc zdarzen nie ma znaczenia, a liczy sie elastyczna skalowalnosc. ## Pytania rekrutacyjne o Kafka dla inzynierow danych Rozmowy kwalifikacyjne na stanowiska z zakresu data engineeringu regularnie obejmuja zaawansowane pytania dotyczace Apache Kafka. Ponizsze pytania pokrywaja najczesciej weryfikowane zagadnienia w 2026 roku. Wiecej pytan cwiczeniowych dostepnych jest w zbiorze [pytan rekrutacyjnych z data engineeringu](/pl/technologies/data-engineering). **W jaki sposob Kafka gwarantuje kolejnosc wiadomosci?** Kolejnosc jest gwarantowana wylacznie w obrebie jednej partycji. Wszystkie wiadomosci z identycznym kluczem partycjonowania trafiaja do tej samej partycji i sa przetwarzane w kolejnosci produkcji. Miedzy roznymi partycjami nie istnieje zadna gwarancja porzadku. Aby uzyskac scisla globalna kolejnosc, konieczny jest topic z jedna partycja, co jednak eliminuje wszelka rownoleglosc przetwarzania. **Czym roznia sie semantyki at-least-once, at-most-once i exactly-once?** At-most-once: konsument zatwierdza offset przed przetworzeniem wiadomosci; w przypadku awarii wiadomosc zostaje bezpowrotnie utracona. At-least-once: konsument zatwierdza offset po przetworzeniu; w razie awarii i restartu wiadomosc jest przetwarzana ponownie. Exactly-once: polaczenie transakcji producenta, idempotencji i izolacji `read_committed` gwarantuje, ze kazda wiadomosc zostanie przetworzona dokladnie raz, nawet w przypadku awarii poszczegolnych komponentow pipeline'u. **Jak dziala rebalancing w consumer group?** Kiedy konsument dolacza do grupy lub ja opuszcza, Kafka inicjuje rebalancing. Podczas tej fazy wszystkie partycje sa tymczasowo zwalniane i ponownie przydzielane. Rebalancing powoduje chwilowe wstrzymanie przetwarzania dla calej grupy. Strategia Cooperative Sticky Assignor minimalizuje ten wplyw, ponownie przydzielajac wylacznie te partycje, ktore faktycznie wymagaja zmiany przypisania. **Co dzieje sie po awarii brokera hostujacego liderow partycji?** Kafka wybiera nowych liderow sposrod zsynchronizowanych replik (ISR -- In-Sync Replicas). Producenci i konsumenci sa automatycznie przekierowywani do nowych liderow. Jesli po awarii warunek `min.insync.replicas` nie jest spelniony, produkcje z ustawieniem `acks=all` koncza sie bledem az do ponownej synchronizacji wystarczajacej liczby replik. **Kiedy wybrac Kafka Streams zamiast Apache Flink?** Kafka Streams to biblioteka osadzana bezposrednio w aplikacji, niewymagajaca oddzielnego klastra przetwarzania. Nadaje sie do transformacji o umiarkowanej zlozonosci z przepustowoscia do kilkuset tysiecy wiadomosci na sekunde. Apache Flink to autonomiczny, rozproszony silnik przetwarzania strumieniowego, przeznaczony do zlozonych operacji (zaawansowane okienkowanie, Complex Event Processing, laczenia wielu strumieni) i ekstremalnych wolumenow danych. Decyzja zalezy od zlozonosci logiki przetwarzania i gotowosci zespolu do utrzymywania dodatkowej infrastruktury. ## Podsumowanie Apache Kafka ugruntowala pozycje referencyjnej platformy strumieniowego przetwarzania danych w ekosystemie data engineeringu. Kluczowe wnioski z niniejszego artykulu: - Tryb KRaft (Kafka 4.x) calkowicie eliminuje ZooKeepera, upraszczajac wdrozenie i administracje klastrow bez utraty gwarancji niezawodnosci - Klucz partycjonowania determinuje kolejnosc przetwarzania, rozklad obciazenia i skalowalnosc -- zle dobrany klucz prowadzi do hot partitions degradujacych wydajnosc calego systemu - Reczne zatwierdzanie offsetow z `enable.auto.commit: False` i izolacja `read_committed` stanowia fundament niezawodnego pipeline'u at-least-once - CDC za posrednictwem Debezium i Kafka Connect przechwytuje zmiany w czasie rzeczywistym z WAL bazy zrodlowej, bez wplywu na jej wydajnosc operacyjna - Transakcje Kafka (exactly-once) gwarantuja atomowosc wzorca consume-transform-produce, przy narzucie kalibrowanym rozmiarem paczek transakcyjnych - Share Groups (KIP-932) w Kafka 4.x otwieraja nowy model konsumpcji przystosowany do dystrybucji zadan bez wymogu zachowania kolejnosci - Monitorowanie consumer lagu, partycji z nieszsynchronizowanymi replikami i opoznien zapytan jest niezbedne do zapobiegania cichym incydentom produkcyjnym --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/data-engineering/apache-kafka-data-engineering-streaming-partitions