---
title: Kafka アーキテクチャ
url: https://doc.liz6.com/ja/distributed-systems/07-messages-and-streams/02-kafka-architecture
locale: ja
area: distributed-systems
tags:
- Kafka
- partition
- ISR
- consumer group
- rebalance
- offset commit
- log compaction
- distributed-systems
- messages-and-streams
date: 2026-06-30
modified: 2026-07-16
description: Kafka はメッセージシステムを、消費後に削除されるキューではなく、不変でパーティション化され、再生可能なログとしてモデル化します。パーティション内の順序保証により同じキーのメッセージの順序が保たれ、ISR メカニズムによりレプリケーションと永続性のトレードオフが制御され、consumer group によりパーティション所有権の調整が分散協調の問題に変換されます。
---

# Kafka アーキテクチャ

> Kafka はメッセージシステムを、消費後に削除されるキューではなく、不変でパーティション化され、再生可能なログとしてモデル化します。パーティション内の順序保証により同じキーのメッセージの順序が保たれ、ISR メカニズムによりレプリケーションと永続性のトレードオフが制御され、consumer group によりパーティション所有権の調整が分散協調の問題に変換されます。

## Partition: 順序付き不変ログ

```
Topic = 複数の partition:
  Partition 0: [offset 0][offset 1][offset 2]...[offset N]  ← 追記のみ
  Partition 1: [offset 0][offset 1][offset 2]...[offset N]
  Partition 2: [offset 0][offset 1][offset 2]...[offset N]

Producer: キーに基づいて partition を選択: partition = murmur2(key) % N_partitions
  → 同じキーは同じ partition へ → そのキーのメッセージは順序付き
  → key=null → ラウンドロビン (負荷分散、順序なし)

Consumer: pull ベース、offset は consumer によって管理される
```

## ISR (In-Sync Replica)

各 partition には 1 つの leader replica と N 個の follower replicas があります。ISR 内の replicas のみが「同期済み」と見なされます：

```
Leader: producer が書き込み → leader: ログへの追記 → followers: leader からフェッチ
  → follower が確認: 「オフセット X までレプリケートしました」
  → replica.lag.time.max.ms 以内に follower が確認しない場合 → ISR から削除
  → min.insync.replicas: 各メッセージはコミットされるまでに、少なくともこの数の ISR コピーの確認が必要
```

leader がクラッシュした場合、新しい leader は ISR 内の follower から選出されます。ISR 外の follower は切り捨てのリスクがあります（ログが leader と分岐するため）。

## Consumer Group

1 つの topic の partition は、同じ consumer group 内の**1つの** consumer によってのみ消費されます。partition は group のメンバー間で割り当てられます。

<svg viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="Consumer Group 分区分配与 crash 后的 rebalance">
  <defs><marker id="kfarr" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker></defs>
  <rect width="720" height="320" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">Consumer Group: 1つのpartitionはグループ内の1つのconsumerのみが消費可能</text>
  <rect x="190" y="46" width="340" height="34" rx="8" fill="#4f46e5"/>
  <text x="360" y="68" text-anchor="middle" font-size="13" font-weight="700" fill="#ffffff">Topic: 3つのpartition</text>
  <line x1="250" y1="80" x2="120" y2="122" stroke="#475569" stroke-width="1.6" marker-end="url(#kfarr)"/>
  <line x1="360" y1="80" x2="360" y2="122" stroke="#475569" stroke-width="1.6" marker-end="url(#kfarr)"/>
  <line x1="470" y1="80" x2="600" y2="122" stroke="#475569" stroke-width="1.6" marker-end="url(#kfarr)"/>
  <text x="172" y="98" text-anchor="middle" font-size="10" fill="#64748b">P0</text>
  <text x="378" y="98" text-anchor="middle" font-size="10" fill="#64748b">P1</text>
  <text x="548" y="98" text-anchor="middle" font-size="10" fill="#64748b">P2</text>
  <rect x="30" y="124" width="180" height="58" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="120" y="150" text-anchor="middle" font-size="13" font-weight="700" fill="#115e59">Consumer 1</text>
  <text x="120" y="170" text-anchor="middle" font-size="11" fill="#0f766e">P0を消費</text>
  <rect x="270" y="124" width="180" height="58" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="360" y="150" text-anchor="middle" font-size="13" font-weight="700" fill="#115e59">Consumer 2</text>
  <text x="360" y="170" text-anchor="middle" font-size="11" fill="#0f766e">P1を消費</text>
  <rect x="510" y="124" width="180" height="58" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="600" y="150" text-anchor="middle" font-size="13" font-weight="700" fill="#115e59">Consumer 3</text>
  <text x="600" y="170" text-anchor="middle" font-size="11" fill="#0f766e">P2を消費</text>
  <text x="360" y="200" text-anchor="middle" font-size="11" fill="#64748b">各partitionは1つのconsumerのみによって消費される → そのpartition内のメッセージ順序は保証される</text>
  <rect x="110" y="218" width="220" height="44" rx="8" fill="#ffedd5" stroke="#f97316"/>
  <text x="220" y="238" text-anchor="middle" font-size="12" font-weight="700" fill="#9a3412">Consumer 2 クラッシュ</text>
  <text x="220" y="254" text-anchor="middle" font-size="11" fill="#c2410c">リバランストリガー</text>
  <line x1="330" y1="240" x2="386" y2="240" stroke="#475569" stroke-width="1.6" marker-end="url(#kfarr)"/>
  <rect x="390" y="218" width="220" height="44" rx="8" fill="#ccfbf1" stroke="#99f6e4"/>
  <text x="500" y="238" text-anchor="middle" font-size="12" font-weight="700" fill="#115e59">Consumer 1 または 3 に割り当て</text>
  <text x="500" y="254" text-anchor="middle" font-size="11" fill="#0f766e">P2を引き継ぐ</text>
  <rect x="30" y="278" width="660" height="34" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="360" y="299" text-anchor="middle" font-size="12" fill="#3730a3">リバランスはpartitionの所有権のみを再割り当てし、同時に各partitionは1つのconsumerのみによって消費され続け、順序保証は影響を受けない</text>
</svg>

Consumer group は**順序**を保証します：各 partition には 1 つの consumer しかいないため、consumer が見るその partition のメッセージは順序付けられています。

### Offset Commit

Consumer は offset を自身で管理します — broker からのプッシュには依存しません。通常、offset は Kafka 内部の `__consumer_offsets` topic に保存されます：

```
Consumer: poll() → N件のメッセージ取得 → 処理 → commitSync(offsets)
  → enable.auto.commit=false: 手動コミット (推奨、処理成功後)
  → enable.auto.commit=true: 定期的な自動コミット (メッセージ消失の可能性あり)
```

## Log Compaction

時間ベースではなく、キーに基づいて最後の値を保持します：

```
圧縮前のログ:
  key=A, value=X
  key=B, value=Y
  key=A, value=Z    ← key A の最新値

圧縮後のログ:
  key=B, value=Y
  key=A, value=Z

→ 適している: KTable (更新ストリーム、最新状態)
→ 適していない: KStream (イベントストリーム、各変更は独立したイベント)
```

## 参考

- **Kafka**: kafka.apache.org/documentation/#design
- **ISR**: kafka.apache.org/documentation/#replication

*キーワード: Kafka, partition, ISR, consumer group, rebalance, offset commit, log compaction*
