Learning Platform
Глоссарий Troubleshooting
Урок 05.02 · 30 мин
Средний
AvroSchemaType SystemPrimitivesComplex TypesLogical TypesAvro IDLNamespaces

Система типов и схема

Схема — центральная концепция Avro

В Avro схема определяет всё: как данные сериализуются, десериализуются, и как работает эволюция. В отличие от Protobuf и Thrift, где схема компилируется в код заранее, Avro-схема — JSON-документ, который можно обрабатывать динамически. Это позволяет строить generic tools (ридеры, конвертеры, валидаторы), которые не знают конкретную схему на этапе компиляции.

Два формата описания схем:

  • JSON — основной, machine-readable формат. Все Avro-инструменты работают с JSON-схемами
  • Avro IDL — человекочитаемый, C-подобный синтаксис. Компилируется в JSON-схему

Примитивные типы

Avro определяет 8 примитивных типов. Каждый тип имеет фиксированное бинарное представление:

ТипРазмер в binary encodingОписание
null0 байтОтсутствие значения. Ничего не пишется.
boolean1 байт0x00 = false, 0x01 = true
int1–5 байт32-bit signed, zigzag + variable-length
long1–10 байт64-bit signed, zigzag + variable-length
float4 байтаIEEE 754 single-precision, little-endian
double8 байтIEEE 754 double-precision, little-endian
bytesvariablelength (Avro long) + raw bytes
stringvariablelength (Avro long) + UTF-8 bytes
Примитивные типы Avro и их размеры
null0 байт на wire. Используется в union с другим типом для optional-полей. Самый экономичный тип — ничего не записывается.
booleanРовно 1 байт: 0x00 или 0x01. Без variable-length encoding — булев тип всегда фиксированный.
intZigzag encoding: (n << 1) ^ (n >> 31). Затем variable-length encoding (группы по 7 бит). Маленькие числа = меньше байт.
longZigzag encoding: (n << 1) ^ (n >> 63). Variable-length — до 10 байт для максимальных значений. Аналогично Protobuf sint64.
floatIEEE 754 single-precision, little-endian. Всегда ровно 4 байта. Порядок байт — little-endian (LSB первый).
doubleIEEE 754 double-precision, little-endian. Всегда ровно 8 байт. Тот же порядок что float — least significant byte first.
bytesAvro long (длина) + raw bytes. Длина — количество байт. Нет ограничений на содержимое — произвольные бинарные данные.
stringAvro long (длина в байтах) + UTF-8 encoded string. Длина — в байтах, не в символах. Мультибайтовые символы увеличивают длину.
TIP

Примитивные типы в JSON-схеме записываются как строки: "null", "int", "string". Но могут быть записаны и как объекты: {"type": "int"}. Вторая форма нужна для добавления логических типов — {"type": "int", "logicalType": "date"}. Обе формы эквивалентны для базовых типов.

Сложные типы

Avro определяет 6 сложных типов. Каждый описывается JSON-объектом со свойством "type":

Record

Именованный набор полей — основной тип для структурированных данных. Аналог struct в C, class в Java, message в Protobuf:

{
 "type": "record",
 "name": "User",
 "namespace": "com.example.avro",
 "doc": "A user record",
 "fields": [
 {"name": "id", "type": "long"},
 {"name": "name", "type": "string"},
 {"name": "email", "type": ["null", "string"], "default": null},
 {"name": "age", "type": "int", "default": 0}
 ]
}

Каждое поле имеет: name (обязательно), type (обязательно), default (опционально — критично для эволюции), doc, order, aliases.

Структура JSON-схемы Record
Record: { type, name, namespace?, doc?, aliases?, fields }Корневой объект схемы. Свойство 'type' = 'record' определяет что это record. 'name' и 'fields' — обязательны. 'namespace', 'doc', 'aliases' — опциональны.
Field DefinitionКаждый field — объект с обязательными 'name' и 'type'. 'default' нужен для schema evolution (добавление поля). 'order' задаёт сортировку. 'aliases' — альтернативные имена.
Field TypeТип поля — любая Avro-схема: примитив ('string'), сложный тип ({'type':'array',...}), именованный тип по fullname, или union (JSON-массив типов).

Enum

Фиксированный набор символьных имён. Значение кодируется как 0-based index:

{
 "type": "enum",
 "name": "Status",
 "symbols": ["ACTIVE", "INACTIVE", "DELETED"],
 "default": "ACTIVE"
}

ACTIVE = 0, INACTIVE = 1, DELETED = 2 на wire. Поле default (с Avro 1.9) используется при schema evolution — если ридер встречает неизвестный символ, подставляется default.

Array

Упорядоченная коллекция элементов одного типа. Кодируется как серия blocks:

{
 "type": "array",
 "items": "string"
}

Map

Набор пар key-value. Ключ всегда string. Значение — произвольный тип:

{
 "type": "map",
 "values": "long"
}
WARNING

Ключи Avro map — строго string. Нельзя создать map<int, string> или map<bytes, long>. Это ограничение спецификации (JSON-ключи — всегда строки). Если нужна map с нестроковым ключом — используйте array of records.

Union

Тип, который может быть одним из перечисленных типов. Описывается JSON-массивом:

["null", "string"]

Это union из null и string — аналог Optional<String>. На wire: индекс типа (0 = null, 1 = string) + значение. Union не может содержать другой union (нет вложенных union) и не может содержать два типа с одним именем.

Fixed

Фиксированное количество байт. Аналог byte[N], записывается без length prefix:

{
 "type": "fixed",
 "name": "MD5",
 "size": 16
}
Иерархия типов Avro

Примитивные (8)

int, long (variable-length zigzag)

Числовые типы с variable-length encoding. int — 32-bit zigzag (1–5 байт), long — 64-bit zigzag (1–10 байт). Экономичны для малых значений.

float, double (fixed IEEE 754)

IEEE 754 floating point, little-endian. float — 4 байта, double — 8 байт. Фиксированный размер, в отличие от int/long.

null, boolean, bytes, string

boolean — 1 байт. null — 0 байт. bytes — length + raw. string — length + UTF-8. Четыре типа с разными стратегиями кодирования.

Сложные (6)

record, enum, fixed (named)

Named types — имеют имя и опционально namespace. Могут быть referenced по имени из других типов. Record, Enum, Fixed — всё named.

array, map (containers)

Container types — содержат элементы другого типа. Array — упорядоченная коллекция. Map — key(string)-value пары. Оба используют block encoding.

union (tagged choice)

Union — JSON-массив типов. На wire: index + value. Не может содержать другой union. Каноническое использование: ['null', 'type'] для optional полей.

Named Types и Namespaces

Три типа в Avro являются named: record, enum, fixed. Они имеют:

  • name — обязательное имя типа
  • namespace — опциональное пространство имён (Java-style: com.example.avro)
  • aliases — список альтернативных имён для schema evolution
  • doc — документация

Полное имя (fullname) = namespace.name. Если namespace не указан, наследуется от ближайшего enclosing named type:

{
 "type": "record",
 "name": "Event",
 "namespace": "com.example",
 "fields": [
 {
 "name": "header",
 "type": {
 "type": "record",
 "name": "Header",
 "fields": [
 {"name": "timestamp", "type": "long"},
 {"name": "source", "type": "string"}
 ]
 }
 }
 ]
}

Здесь Header наследует namespace com.example → fullname = com.example.Header. Вложенный record можно ссылать по fullname из других типов: "type": "com.example.Header".

Aliases позволяют переименовывать типы и поля при schema evolution:

{
 "type": "record",
 "name": "UserEvent",
 "aliases": ["UserAction", "com.legacy.UserEvent"],
 "fields": [
 {"name": "userId", "type": "long", "aliases": ["user_id", "uid"]}
 ]
}

Ридер со схемой UserAction успешно прочитает данные, записанные со схемой UserEvent — alias разрешается при schema resolution.

Avro IDL

JSON-схемы verbose — Avro IDL предоставляет человекочитаемую альтернативу с C-подобным синтаксисом:

@namespace("com.example.avro")
protocol UserProtocol {

 enum Status {
 ACTIVE, INACTIVE, DELETED
 }

 record User {
 long id;
 string name;
 union { null, string } email = null;
 int age = 0;
 Status status = "ACTIVE";
 array<string> tags = [];
 }

 record Event {
 long timestamp;
 string type;
 union { null, User } user = null;
 }
}

IDL компилируется в JSON-схему утилитой avro-tools:

java -jar avro-tools.jar idl2schemata user.avdl output/
TIP

IDL удобен для human review и version control (чистые диффы). Но runtime-инструменты (Schema Registry, Kafka serializers, Avro readers) работают только с JSON-схемами. IDL — это authoring format, не wire format.

Логические типы

Логические типы — семантическая надстройка над примитивными и fixed типами. Физическое представление одно и то же, но ридер интерпретирует значение по-другому:

Логические типы Avro

Пример JSON-схемы с логическими типами:

{
 "type": "record",
 "name": "Transaction",
 "fields": [
 {
 "name": "amount",
 "type": {
 "type": "bytes",
 "logicalType": "decimal",
 "precision": 10,
 "scale": 2
 }
 },
 {
 "name": "created_at",
 "type": {"type": "long", "logicalType": "timestamp-millis"}
 },
 {
 "name": "date",
 "type": {"type": "int", "logicalType": "date"}
 },
 {
 "name": "id",
 "type": {"type": "string", "logicalType": "uuid"}
 }
 ]
}
NOTE

Логический тип duration использует fixed из 12 байт — три 32-bit unsigned little-endian значения: months, days, milliseconds. Это не стандартный Unix duration — месяцы и дни хранятся отдельно, потому что их длина непостоянна. Модель аналогична ISO 8601 duration P1M2DT3H → months=1, days=2, millis=10800000.

Каноническая форма схемы

Avro определяет Parsing Canonical Form (PCF) — нормализованное JSON-представление схемы, используемое для вычисления fingerprint:

Правила нормализации:

  1. Удалить все свойства кроме type, name, fields, symbols, items, values, size
  2. Из полей record оставить только name и type (убрать default, doc, aliases, order)
  3. Заменить именованные типы на fullname при повторном использовании
  4. Убрать пробелы

Fingerprint вычисляется как 64-bit Rabin hash от PCF — используется в Single Object Encoding (урок 05) для идентификации схемы по 8-байтовому значению.

Сравнение с другими системами типов

АспектAvroProtobufThrift
Описание схемыJSON / IDL.proto файлы.thrift файлы
КодогенерацияОпциональнаяОбязательнаяОбязательная
Идентификация полейПо имени + позицииПо числовому tagПо числовому ID
Nullableunion с nulloptional + wrapper typesoptional
Map key typesТолько stringint32, int64, string, boolЛюбой тип
Наследование
Default valuesВ JSON-схемеВ .proto файлеВ .thrift файле
ЭволюцияПо имени поля + defaultsПо field tagПо field ID

Ключевые выводы

  1. 8 примитивных типов: null, boolean, int, long, float, double, bytes, string — каждый с фиксированным binary encoding
  2. 6 сложных типов: record (named struct), enum (indexed symbols), array (block-encoded list), map (string keys only), union (tagged choice), fixed (byte array)
  3. Named types (record, enum, fixed) имеют namespaces и aliases для schema evolution
  4. Avro IDL — человекочитаемая альтернатива JSON-схемам, компилируется в JSON
  5. Логические типы — семантическая надстройка: decimal, date, timestamps, uuid. Физический тип не меняется
  6. Parsing Canonical Form — нормализованная схема для вычисления 64-bit Rabin fingerprint

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Avro определяет 8 примитивных типов. Какие из них используют variable-length encoding (количество байт зависит от значения)?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 6