Hello, Avro!
μ Avroλ₯Ό μ°κ² λμλμ??
λ°μ΄ν° μμ§λμ΄λ‘ μΌμ νλ©΄μ νμ¬ μ΄λ²€νΈ νμ΄νλΌμΈμ λλ§‘μμ κ΄λ¦¬νκ³ μμ΅λλ€. ν루 3μ΅ κ±΄ μ λμ λ°μ΄ν°λ₯Ό λ°μμ Kafkaμ λ°μ΄λ£κ³ μλλ°μ! μ΄ κ³Όμ μμ AvroSerializerλ‘ λ°μ΄ν°λ₯Ό μΈμ½λ©νμ¬ Kafkaμ 보λ΄κ³ μμ΅λλ€.
κ·Έλ°λ°, μ΄λ μκ°!! μ Avro ν¬λ§·μ μ°λ κ±ΈκΉ?λΌλ μκ°μ΄ λ€μμ΅λλ€. 1λ λκ² μ΄λ²€νΈ νμ΄νλΌμΈμ κ΄λ¦¬νκ³ μμλλ°, Avroκ° λμ§ μ λλ‘ μμ§λ λͺ»νλ μνμμ Avroλ₯Ό μ°κ³ μλ€λ κ±Έ λ¬Έλ κΉ¨λ«κ² λμμ΅λλ€. μ΄λ² ν¬μ€νΈλ βμμ§ λΆμ‘±νκ² λ§κ΅¬λβλΌλ λ§μ, κ·Έλ¦¬κ³ βμ¬μ ν μ¬λ°λ κ²λ€μ΄ λ¨μκ΅°!βλΌλ μκ°μΌλ‘ μ 리ν΄λ³΄κ² μ΅λλ€.
What is Avro
JSON, CSVλ₯Ό λ΄μ λ°μ΄ν° νμμ
λλ€. νΉμ΄ν μ μ μ€ν€λ§ μ λ³΄κ° λ°μ΄ν° νμΌμ ν¨κ» λ΄κΈ΄λ€λ μ μ
λλ€. μ¦, xxxx.avroλΌλ νμΌ μμ μλμ κ°μ μ€ν€λ§ μ 보λ₯Ό λ΄μ μ μμ΅λλ€.
{
"namespace": "NAMESPACE",
"name": "NAME",
"doc": "Any Commentary",
"type": "record",
"fields": [
{"name": "name", "type": "string"},
{"name": "age", "type": "int"},
]
}
μ΄λ, avroμ μ€ν€λ§ μ 보λ .avscλΌλ Avro SchemaλΌλ ν¬λ§·μ λ§μΆ° μμ±λ©λλ€.
OβReilly - Operationalizing the Data Lake
Letβs Start Avro
Avro ν¬λ§·μ μ¬μ©νλ μ¬λ¬ λ°©λ²μ΄ μμ§λ§, μ΄λ² ν¬μ€νΈμμλ PythonμΌλ‘ μ§νν©λλ€. μ΄λ₯Ό μν΄ fastavroλΌλ ν¨ν€μ§λ₯Ό μ€μΉν©λλ€. μ λ μμ± μμ κΈ°μ€μΌλ‘ μ΅μ λ²μ μΈ 1.9.7 λ²μ μΌλ‘ μ§ννμ΅λλ€.
pip install fastavro==1.9.7
Avro Write
from fastavro import parse_schema, writer
schema = {
"namespace": "earth",
"name": "person",
"doc": "person information",
"type": "record",
"fields": [
{"name": "name", "type": "string"},
{"name": "age", "type": "int"},
]
}
parsed_schema = parse_schema(schema)
message = {
"name": "Jobs",
"age": 26,
}
with open("./hello.avro", "wb") as f:
writer(f, parsed_schema, [message] * 100)
κ°λ¨ν©λλ€. fastavroμ writer() ν¨μμ μ€ν€λ§μ μ μ₯ν λ°μ΄ν°λ₯Ό μ λ¬ν©λλ€.
Avro Read
from fastavro import reader
with open("hello.avro", "rb") as f:
_reader = reader(f)
print(_reader)
print(_reader.writer_schema)
print(_reader.reader_schema)
for record in _reader:
print(record)
break
Avro νμΌμλ μ€ν€λ§ μ λ³΄κ° μ΄λ―Έ λ΄κ²¨ μκΈ° λλ¬Έμ, μ½μ λλ μ€ν€λ§λ₯Ό μ§μ ν νμκ° μμ΅λλ€.
Avro Schema
Avro μ€ν€λ§λ JSON ν¬λ§·μΌλ‘ μμ±λ©λλ€.
{
"namespace": "earth",
"name": "person",
"doc": "person information",
"type": "record",
"fields": [
{"name": "name", "type": "string"},
{"name": "age", "type": "int"},
]
}
.avro νμΌμ μΆλ ₯ν΄λ³΄λ©΄, Avro Schemaλ λ°μ΄λλ¦¬κ° μ§λ ¬νλμ§ μκ³ , κ·Έ κ° κ·Έλλ‘ μ‘΄μ¬ν©λλ€.

Avro Schemaμ μ‘΄μ¬νλ nameκ³Ό namespace νλλ μ€ν€λ§λ₯Ό μλ³νκΈ° μν μλ³μ μ
λλ€. μ λ μ½κ² μκ°ν΄ nameμ΄ RDBμ Table, namespaceκ° RBDμ databaseμ λμλλ κ°λ
μ΄λΌκ³ μ΄ν΄νμ΅λλ€.
Reject Schema Mismatch when AVRO Write
message = {
"name": "Jobs",
# no `age` field
}
fastavro.write()λ₯Ό ν λ, μ μ₯ν λ©μμ§κ° μ€μ ν μ€ν€λ§μ λΆμΌμΉ νλ€λ©΄, avro writeμ΄ μΌμ΄λμ§ μμ΅λλ€. μλ₯Ό λ€μ΄, μ€ν€λ§μλ age νλκ° μ μλμ΄ μλλ°, λ©μμ§μλ μλ€λ©΄ ValueError: no value and no default for age Exceptionμ λ³΄κ² λ©λλ€.
λ°λλ‘, λ©μμ§μ μ μνμ§ μμ 컬λΌμ΄ μΆκ°λ‘ λ€μ΄μ€λ κ²½μ°λ
message = {
"name": "Jobs",
"age": 26,
"address": "USA" # new field, but not defined
}
Avro writeλ μ±κ³΅νμ§λ§, μ μ₯λ λ°μ΄ν°λ₯Ό μ½μ΄λ³΄λ©΄, μ μνμ§ μμ νλλ .avroμ μ μ₯λμ§ μμ΅λλ€.
Rejectionμ νμ μ λν΄μλ μΌμ΄λλλ°,
message = {
"name": "Jobs",
"age": "26" # string, but defined as int
}
ageλ₯Ό intκ° μλ stringμΌλ‘ μ λ¬νλ©΄ TypeError: an integer is required on field ageλΌλ Exceptionμ λ³΄κ² λ©λλ€.
Nullable Field
Avroμμ μ΄λ€ νλκ° Nullable νμ§λ μλμ κ°μ μ€ν€λ§λ‘ ννν μ μμ΅λλ€.
{
"namespace": "earth",
"name": "person",
"doc": "person information",
"type": "record",
"fields": [
{"name": "name", "type": "string"},
{"name": "age", "type": ["null", "int"]},
]
}
μ΄λ° κ²½μ°, age νλκ° nullable μ΄κΈ° λλ¬Έμ, ν΄λΉ νλμ κ°μ΄ μμ΄λ avro writeκ° κ°λ₯ν©λλ€.
Union Field
μ¬μ€ μμμ nullable νλλ₯Ό νννλ λ°©λ²μ Avro μ€ν€λ§μ βUnionβ νμ μ νμ©ν κ²μ λλ€. Union νμ μ νλμ νλμ μ¬λ¬ λ°μ΄ν° νμ μ μΈ μ μλλ‘ νμ©ν©λλ€.
{
"namespace": "earth",
"name": "person",
"doc": "person information",
"type": "record",
"fields": [
{"name": "name", "type": "string"},
{"name": "age", "type": ["null", "int", "string"]},
]
}
κ·Έλμ μ΄λ κ² ageκ° intμ΄λ©΄μ, stringμ΄λ©΄μ, nullableμΈ κ²½μ°λ κ°λ₯ν©λλ€!!
Schema Reference
Avroμμλ μ μν μ€ν€λ§λ₯Ό λ€λ₯Έ μ€ν€λ§μ νλ κ°μ²΄λ‘ μ¬μ©νλ κ²λ κ°λ₯νλ€. μλ μμ λ earth.animalλΌλ μ€ν€λ§λ₯Ό μ μνκ³ , μ΄λ₯Ό earth.personλΌλ μ€ν€λ§μ pet νλμ νμ
μΌλ‘ μ¬μ©νλ μμ μ΄λ€.
schema = [{
"namespace": "earth",
"name": "animal",
"doc": "animal information",
"type": "record",
"fields": [
{"name": "pet_name", "type": "string"},
{"name": "type", "type": ["null", "string"]},
]
}, {
"namespace": "earth",
"name": "person",
"doc": "person information",
"type": "record",
"fields": [
{"name": "name", "type": "string"},
{"name": "age", "type": ["null", "int", "string"]},
{"name": "pet", "type": ["null", "earth.animal"]},
]
}]
message = [{
"name": "John",
"age": "30",
"pet": {
"pet_name": "Maru",
"type": "dog"
}
}]
Union Schema
μμ Schema Reference μΌμ΄μ€μμλ xxxx.avro νμΌμ΄ νλμ earth.petκ³Ό earth.person, λ κ°μ avro μ€ν€λ§κ° ν€λμ μ μ₯λ κ² μ
λλ€. μμ μμμμλ earth.personμ λν λ°μ΄ν°λ₯Ό μ μ₯νλ κ²μ²λΌ μ μμ§λ§, μλμ κ°μ΄ earth.petκ³Ό earth.person λ°μ΄ν°λ₯Ό νλμ avro νμΌμ μ μ₯νλ κ²λ κ°λ₯ν©λλ€.
...
message = [{
"name": "John",
"age": "30",
"pet": {
"name": "Maru",
"type": "dog"
}
}, {
"pet_name": "Maru-2",
"type": "human?"
}]
μνΌ μ νκ³ μΆμ λ§μ Avroλ λ€μ€ μ€ν€λ§λ μ§μν©λλ€!
λ§Ίμλ§
Avroλ λͺ λ°±ν Schemaκ° μλ μλ£κ΅¬μ‘°μ§λ§, Union Fieldλ Union Schemaμ μΌμ΄μ€λ₯Ό 보면 λ€μ€ νμ μ΄λ λ€μ€ μ€ν€λ§λ₯Ό μ§μνλ€λ μ μ°μ±λ κ°μΆκ³ μμ΅λλ€.
ν¬μ€νΈλ₯Ό μμ±νλ©΄μ μ§κΈμ μ΄λ²€νΈ νμ΄νλΌμΈ μ½λκ° μ΄λ€ μλλ‘ μ»€μ€ν μ νλμ§ μμΈν μ΄ν΄λ³΄λ κ³κΈ°κ° λ κ² κ°μ΅λλ€. μμΌλ‘ μ’ μ’ Kafkaλ₯Ό 곡λΆνλ©° μ νλ μ§μλ€μ μ 리ν΄λ³΄κ² μ΅λλ€ γ γ