スキーマ参照は、共通の型定義を複数スキーマで再利用するためのSchema Registry機能です。重複定義をなくし、変更時の影響範囲を明確にできます。
参照は“特定のサブジェクトとその版”に固定されます。最新へ自動追従しないため、計画的な移行が可能です。
スキーマ参照(References)は、Schema Registryに登録済みの別スキーマを名前で呼び出して再利用する仕組みです。対象はAvro、Protobuf、JSON Schemaで、共通アドレス型や共通エラーフォーマットなどの“横断的な型”を一元管理できます。
参照は、ルート(親)スキーマに対して、参照名・参照先サブジェクト・参照先バージョンを紐づけて登録します。実行時のKafkaメッセージには、ルートのスキーマIDのみが書き込まれ、デシリアライザはRegistryから参照を解決して完全なスキーマを取得します。
利点は、型定義の重複削減、変更の局所化、レビューの容易化、互換性チェックの一元化です。欠点は、登録順序の管理(先に共通型→後にルート)と、参照バージョンの明示的な更新作業が必要な点です。
登録手順はシンプルです。1) まず共通型(例:Address)を単独のサブジェクトとして登録。2) 次に、共通型を使うルートスキーマ(例:Order)を、referencesに共通型の“サブジェクト名とバージョン”を指定して登録します。互換性チェックは解決後の“完全展開スキーマ”に対して行われます。
参照のnameの意味はフォーマットにより異なります。一般ガイドとして、Avroは完全修飾型名、Protobufはimport名(.proto)、JSON Schemaは$refで参照する識別子名やパスに合わせるのが通例です。
実行時は、プロデューサ/コンシューマともにルートのSchema IDのみを用い、Schema Registryが裏側で参照を解決します。したがってクライアントのワイヤ形式は参照の有無で変わりません。
参照解決の流れ(概念図)
Avro 参照付きスキーマの登録例 (Schema Registry REST API)
## 1) 共通型 Address を登録
curl -s -X POST \
-H 'Content-Type: application/vnd.schemaregistry.v1+json' \
http://localhost:8081/subjects/com.example.types.Address/versions \
-d '{
"schemaType": "AVRO",
"schema": "{\n \"type\": \"record\",\n \"name\": \"Address\",\n \"namespace\": \"com.example.types\",\n \"fields\": [\n {\"name\": \"street\", \"type\": \"string\"},\n {\"name\": \"city\", \"type\": \"string\"}\n ]\n}"
}'
## 2) ルート型 Order を登録(Address を参照)
curl -s -X POST \
-H 'Content-Type: application/vnd.schemaregistry.v1+json' \
http://localhost:8081/subjects/com.example.sales.Order/versions \
-d '{
"schemaType": "AVRO",
"references": [
{
"name": "com.example.types.Address",
"subject": "com.example.types.Address",
"version": 1
}
],
"schema": "{\n \"type\": \"record\",\n \"name\": \"Order\",\n \"namespace\": \"com.example.sales\",\n \"fields\": [\n {\"name\": \"id\", \"type\": \"string\"},\n {\"name\": \"shippingAddress\", \"type\": \"com.example.types.Address\"}\n ]\n}"
}'
# 注意: 上記のサブジェクト名は例です。実運用ではSubject Naming Strategy(例: TopicNameStrategyで orders-value 等)が決定します。参照自体の動作は同じです。共通型を複数トピックで再利用する場合、Subject Naming Strategyの選択が重要です。デフォルトのTopicNameStrategyでは、各トピックごとに<topic>-key / <topic>-valueの別サブジェクトとなるため、同じ型でもトピックが違えば別サブジェクトに登録されます。
共通型を“1つのサブジェクト”に集約して再利用したい場合は、RecordNameStrategy(またはTopicRecordNameStrategy)を検討します。RecordNameStrategyは完全修飾レコード名(例: com.example.types.Address)をサブジェクトに使うため、トピックをまたいでも同一型は同一サブジェクトで管理できます。
参照は“subject@version”で固定されるため、共通型の新バージョンを発行しても、各ルートスキーマは明示的に参照を更新するまで既存バージョンを使い続けます。これにより段階的ロールアウトが可能です。
| 項目 | Avro | Protobuf | JSON Schema |
|---|---|---|---|
| 参照の表現 | 命名型の解決(完全修飾名での再利用) | importでの依存解決(.proto) | $refでの参照解決 |
| references.name の意味 | 完全修飾型名(例: com.example.types.Address) | import名(例: common.proto) | $refで使う識別子/パスに合わせる |
| ワイヤ形式 | ルートSchema IDのみ(参照IDは埋め込まれない) | 同左 | 同左 |
| 共通型の再利用に向く命名戦略 | RecordNameStrategy | RecordNameStrategy または TopicNameStrategy + package管理 | RecordNameStrategy(値スキーマでの再利用に有効) |
| 進化の注意点(代表例) | デフォルトなしの新規必須フィールド追加は後方非互換 | 既存field番号の再利用は非互換 | 型の絞り込み(enum縮小など)は非互換になりやすい |
Schema Registryの互換性はサブジェクト単位で設定し、登録時に“解決後の完全スキーマ”に対してチェックされます。参照を含む場合、共通型を取り込んだ状態で比較されるため、共通型の破壊的変更はルートの登録時に検知されます。
互換性レベルにはBackward / Forward / Fullと、それぞれにTransitiveのバリアントがあります。Transitiveを使うと直前バージョンだけでなく“全バージョン”との互換性を検証します。共通型を広く再利用している場合、FULL_TRANSITIVEが安全側です。
参照は“バージョン固定”なので、共通型の新バージョンを公開しても既存のルートは直ちには影響を受けません。段階的に各ルートで参照先バージョンを更新・再登録していく運用が基本です。
共通型の変更は影響が広いので、Compatibilityを厳しめ(例:FULL_TRANSITIVE)に設定し、まず共通型の新バージョンを公開→検証→各ルートで参照を更新→プロデューサを段階的にデプロイ、という順で進めます。
ワイヤ形式はルートのSchema IDのみなので、参照の有無でKafkaメッセージ互換性は変わりません。焦点は“登録可否”と“デシリアライズ可否”です。互換性違反は登録時に止め、コンシューマ側の互換性はスキーマ進化ルール(例:デフォルト値の付与)で担保します。
Subject Naming Strategyの違いと再利用の可否(TopicNameStrategy/RecordNameStrategy/TopicRecordNameStrategy)。
参照の登録時に必要な要素(name/subject/version)と、最新追随がない点。
互換性チェックは“参照解決後”に行われ、Transitive設定の意味は“全履歴との比較”。
CCDAK
問題 1
複数トピックで共通の Address 型を再利用しています。Address に任意フィールドを1つ追加し、すべてのトピックで重複定義なしに共有したい。ワイヤ形式は変えず、段階的に移行したい場合、最も適切なアプローチはどれか?
正解: B
共通型をRecordNameStrategyで1サブジェクトに集約し、参照はsubject@versionで固定します。Addressに後方互換な変更(任意フィールド追加)を行い、新バージョンを公開した後、各ルートスキーマのreferencesで新バージョンを指すように再登録すれば、ワイヤ形式は維持され、段階的移行が可能です。
異なるスキーマタイプ間(例:AvroからProtobuf)で参照できますか?
できません。参照は同一schemaType内で行われ、AvroはAvro、ProtobufはProtobuf、JSON SchemaはJSON Schemaどうしで解決されます。
参照先スキーマを更新したら既存プロデューサは即座に影響を受けますか?
いいえ。参照は特定バージョンに固定されます。既存プロデューサ/ルートスキーマは明示的に参照を更新・再登録しない限り、従来バージョンを使い続けます。
Schema Registryがダウンした場合、参照付きスキーマのデシリアライズはどうなりますか?
クライアントが必要なスキーマをローカルキャッシュしていれば継続できますが、未キャッシュのSchema IDや参照解決が必要な場合はRegistryへのアクセスが必要です。可用性の高いRegistry構成(冗長化)を推奨します。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Kafka Topic と Partition の基礎: 分散とスケーラビリティの要
CCDAK 対策と実務の両立を意識し、Topic/Partition/Replica/Consumer Group の役...
CCDAK 試験ガイド:出題範囲・配点・申込み・対策
Confluent Certified Developer for Apache Kafka (CCDAK) の出題範囲...
Confluent Certified Administrator (CCAAK) 対策: 出題範囲・配点の考え方・運用観点の要点
CCAAKに向けて、試験領域の押さえどころを運用目線で整理。プロダクションで通用する設定・監視・セキュリティの実践知を、...
Kafka の Replica と In-Sync Replicas を正しく設計する: 耐障害性と一貫性
レプリカとISRの仕組みを起点に、acks と min.insync.replicas、クリーン/アンクリーンリーダー選...
Kafka の Offset とコミット: ポジション管理と at-least-once の基礎
CCDAK 対策と実務の両立を意識して、Kafka コンシューマのオフセット管理とコミット戦略を整理。at-least-...