Kafka

Avro スキーマ設計: フィールド追加・デフォルト・互換性の実務と CCDAK 対策

2026-04-19
NicheeLab編集部

Kafka で Avro を使うなら、互換性モードとデフォルト値の扱いを外すと痛い目にあいます。この記事は、フィールド追加・削除・型変更・エイリアス(リネーム)を、Schema Registry の互換性と結びつけて整理します。

CCDAK では、Backwards/Forwards/Full と Transitive の違い、デフォルト値の必須性、null を含む union の並び順が頻出です。現場で壊さない設計と、試験で落とさない要点をセットで確認しましょう。

互換性モードの前提と選び方

Avro の互換性は「ライタースキーマ(writer)のデータを、リーダースキーマ(reader)で読む」際の解決ルールに基づきます。Confluent Schema Registry は新しいスキーマを登録するたびに、選択した互換性モードと既存バージョンを突き合わせて検証します。

一般運用では Backward 互換を採用するケースが多く、先にコンシューマを更新してからプロデューサを更新する流れに合致します。一方、先にプロデューサ更新をしたい場合は Forward、双方向の安全性が必要なら Full を選びます。Transitive は直前だけでなく全バージョンに対して検証する強化オプションです。

  • Backward: 新しいリーダースキーマで、古いデータ(古いライター)を読めることを保証
  • Forward: 古いリーダースキーマで、新しいデータ(新しいライター)を読めることを保証
  • Full: Backward と Forward の両方を満たす
  • Transitive 付き: 直前だけでなく全過去バージョンに対して検証
  • 互換性は subject(例: <topic>-value) 単位で設定可能
互換性モード既存コンシューマへの影響既存プロデューサへの影響代表的に許可される変更例
BACKWARD新スキーマのコンシューマは旧データを読める既存プロデューサは継続稼働可フィールド追加(デフォルト必須)、型の昇格(int→long など)、フィールドのリネーム(エイリアス)
FORWARD旧コンシューマが新データを読める新プロデューサに制約が強いフィールド削除(ただし旧読者側にデフォルトが必要)、将来互換を壊さない限定的変更
FULL旧⇔新どちら向きも読める新旧いずれの変更も厳格に制約追加はデフォルト必須、型変更は昇格のみ、名前変更はエイリアス前提
NONE検証なし。破壊的変更も通る短期実験以外非推奨
TRANSITIVE 拡張全過去版に対して保証リリース列のどこかで破綻しないことを担保BACKWARD_TRANSITIVE / FORWARD_TRANSITIVE / FULL_TRANSITIVE として利用

Schema Registry を介した Avro 読み書きの流れ

register / get IDrecord + schema IDrecord + schema IDfetch writer schema by IDProducer(writer)Schema RegistryIDs, compatibilityKafka brokerConsumer(reader)Reader schema resolutionWriter schema ID はメッセージに同梱。Consumer は ID から writer schema を取得し、自前の reader schema と解決

フィールド追加: デフォルト値の設計指針

最も多い変更が「フィールドの追加」です。Backward 互換を確保するには、追加フィールドにデフォルト値を必ず設定します。旧データにはそのフィールドが存在しないため、リーダー側でデフォルトが使われます。

optional を表すなら union に null を含め、かつ union の先頭に null を置き、default も null にします。Avro の仕様上、union の default は配列の先頭型と一致していなければなりません。

複合型のデフォルト値は制約が多く、特にレコードや配列のデフォルトは空集合か仕様に沿った完全値が必要です。可能なら nullable にして null をデフォルトにする方が保守しやすいです。

  • 追加フィールドは default を必ず付ける(Backward 互換)
  • optional は ["null", "type"] の順で、"default": null
  • 数値はビジネス的に安全なゼロ初期化に安易に逃げない。欠損とゼロは意味が異なることが多い
  • 大きな構造体はまず nullable + null default にし、のちに必須化を検討

フィールド削除・必須化の落とし穴

フィールド削除は Forward 互換の観点で危険です。新ライターがそのフィールドを書かなくなると、旧リーダーはリーダースキーマに default がない限り解決に失敗します。現実には旧リーダーのスキーマを後から直せないため、ストレートな削除は避けます。

安全策は段階的な非推奨化です。まずフィールドを nullable + default null に変更し、コンシューマを全て更新してから、将来のメジャー更新で削除を検討します。必須化も同様に、一度 nullable に戻れない設計は破壊的になります。

  • 削除前に nullable + default null 化 → 全コンシューマ更新 → 監視 → 後日削除
  • 旧コンシューマが期待するフィールドに default がないと Forward 互換は破綻
  • 必須化は最後の手段。入力バリデーションで代替できないか検討

型変更・名前変更・エイリアス

型変更は昇格(promotion)のみが安全です。代表例は int→long→float→double の昇格です。string と bytes の相互昇格は Avro の自動昇格では扱われないため、union で併存させる設計を検討します。

名前変更は aliases を使います。フィールドやレコード名を変更しても、aliases に旧名を列挙しておけば、解決時に旧名フィールドが新名にマッピングされます。これにより Backward 互換を保ったリネームが可能です。

enum は新しいシンボルの追加は Backward 互換(新リーダーは旧データを読める)ですが、Forward 互換では危険です(旧リーダーが未知のシンボルを解釈できない)。

  • 安全な型昇格例: int→long、long→double、float→double
  • 危険: boolean→int、string↔bytes の単純置換、decimal の精度縮小
  • 名前変更は新スキーマ側に aliases: ["旧名"] を付与
  • enum の追加は Backward OK、Forward NG。除去は逆方向で危険

レコード進化をコードで確認

典型的な進化例として、フィールド追加(デフォルト付き)、型昇格、フィールドのリネーム(aliases)を組み合わせます。互換性は Backward に設定し、先にコンシューマを更新してからプロデューサを更新します。

以下は v1→v2 への進化例です。v2 は Backward 互換を満たすため、追加フィールドは nullable + default null、数値は型昇格、リネームには aliases を使用しています。

  • Schema Registry の subject は通常 <topic>-value
  • 互換性は subject 単位で設定し、運用中は変更を厳禁にするのが無難
  • aliases は新スキーマ側に記述して旧名を吸収

Avro スキーマ進化例と互換性設定

# v1: 初期スキーマ
{
  "type": "record",
  "name": "Order",
  "namespace": "nl.shop",
  "fields": [
    {"name": "id", "type": "string"},
    {"name": "amount", "type": "int"},
    {"name": "status", "type": {"type": "enum", "name": "OrderStatus", "symbols": ["NEW", "PAID"]}}
  ]
}

# v2: 追加・昇格・リネーム (Backward 互換)
{
  "type": "record",
  "name": "Order",
  "namespace": "nl.shop",
  "fields": [
    {"name": "order_id", "type": "string", "aliases": ["id"]},
    {"name": "amount", "type": "long"},
    {"name": "status", "type": {"type": "enum", "name": "OrderStatus", "symbols": ["NEW", "PAID", "CANCELLED"]}},
    {"name": "note", "type": ["null", "string"], "default": null}
  ]
}

# ポイント
# - amount: int->long は型昇格
# - order_id: フィールド名の変更だが、aliases で旧名 id を吸収
# - note: ["null", "string"] の順で default は null
# - enum 追加(CANCELLED): Backward は OK。Forward(旧リーダー)は新シンボルを解せない点に注意

# Schema Registry: subject の互換性を Backward に設定 (例: <topic>-value)
# API パスは実環境に合わせる
curl -X PUT \
  -H "Content-Type: application/json" \
  --data '{"compatibility": "BACKWARD"}' \
  http://schema-registry:8081/config/my-orders-value

シリアライザ/レジストリ設定の実務

Confluent の Avro シリアライザ/デシリアライザは、メッセージにスキーマ ID を埋め込み、Registry と連携して writer schema を取得します。SpecificRecord を使う場合は reader schema が生成クラスから与えられ、GenericRecord を使う場合はデシリアライザが writer schema のまま解釈します。

本番では auto.register.schemas を無効にして、スキーマ登録とレビューを別パイプラインで管理するのが安全です。subject の命名戦略は topic 単位(TopicNameStrategy)が一般的ですが、レコード再利用が多い場合は RecordNameStrategy も検討します。

  • schema.registry.url は必須
  • auto.register.schemas=false を基本に、CI で事前登録と互換性検証
  • value.subject.name.strategy: TopicNameStrategy / RecordNameStrategy / TopicRecordNameStrategy
  • specific.avro.reader=true で SpecificRecord を reader schema として使用
  • use.latest.version は安易に使わない。互換性と整合性の検証を通すこと

CCDAK 向けチェックリスト

試験では言葉の定義とセットの暗記で点が伸びます。設問は具体的な変更提案に対して、どの互換性モードで可か不可か、どんなデフォルトが必要かを問う形式が多いです。

  • Backward: フィールド追加は default が必須。union の default は先頭型と一致
  • Forward: フィールド削除は旧リーダーに default がないと失敗しやすい
  • Full: 双方向の条件を同時に満たす必要があり最も制約が強い
  • 型昇格だけが安全(int→long→double)。boolean や string の置換は不可
  • リネームは aliases を新スキーマに付与する
  • enum 追加は Backward OK、Forward NG

問題で確認

CCDAK

問題 1

Value 側 subject が BACKWARD 互換に設定されている。既存の Order レコードに新しい任意フィールド note を追加したい。最も安全な変更はどれか。

  1. note を型 string、default なしで追加する
  2. note を型 ["null", "string"]、default を null で追加する
  3. note を型 ["string", "null"]、default を "" で追加する
  4. note を型 ["null", {"type":"record",...}]、default を {} で追加する

正解: B

Backward 互換でフィールド追加を行う場合、追加フィールドには default が必須。optional にするなら union の先頭を null にし、default も null にする必要がある。よって ["null", "string"] かつ default=null が正解。

よくある質問

union における default の型はどの要素に合わせるべきですか?

Avro 仕様では union の default は配列の先頭型と一致していなければなりません。optional フィールドは ["null", "type"] の順にし、default は null にします。

型変更はどこまで安全ですか?

安全なのは昇格のみです。代表的には int→long、long→double、float→double。string↔bytes、boolean→int のような変更は不可。decimal の精度縮小など論理型の後退も避けてください。

enum のシンボル追加・削除は互換性にどう影響しますか?

シンボルの追加は Backward 互換(新リーダーは旧データを読める)ですが、Forward 互換は満たしません(旧リーダーは新シンボルを解釈できない)。削除はその逆方向で危険度が高く、基本的に避けます。

この記事で学んだ内容を問題で確認しましょう

16,000問以上の問題で実力チェック

無料で問題を解いてみる
この記事の著者

NicheeLab編集部

データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。


関連記事
Kafka

Kafka Topic と Partition の基礎: 分散とスケーラビリティの要

CCDAK 対策と実務の両立を意識し、Topic/Partition/Replica/Consumer Group の役...

Kafka

CCDAK 試験ガイド:出題範囲・配点・申込み・対策

Confluent Certified Developer for Apache Kafka (CCDAK) の出題範囲...

Kafka

Confluent Certified Administrator (CCAAK) 対策: 出題範囲・配点の考え方・運用観点の要点

CCAAKに向けて、試験領域の押さえどころを運用目線で整理。プロダクションで通用する設定・監視・セキュリティの実践知を、...

Kafka

Kafka の Replica と In-Sync Replicas を正しく設計する: 耐障害性と一貫性

レプリカとISRの仕組みを起点に、acks と min.insync.replicas、クリーン/アンクリーンリーダー選...

Kafka

Kafka の Offset とコミット: ポジション管理と at-least-once の基礎

CCDAK 対策と実務の両立を意識して、Kafka コンシューマのオフセット管理とコミット戦略を整理。at-least-...

Kafkaの記事一覧 (101件)
© 2026 NicheeLab All rights reserved.