序列化是把記憶體中的物件變成可傳輸 / 可儲存的 bytes(encoding),再還原回來(decoding)。選格式的本質是在問:要人讀得懂,還是要機器跑得快、傳得省。
🧠 Intuition
TIP
一句話分辨:文字格式把「欄位名」也一起塞進每一筆資料;二進位 schema 格式把欄位名抽成外部 schema,資料裡只剩值(甚至連 tag 都省)。膨脹與效率的差別,幾乎都來自「欄位名要不要重複攜帶」。
- 文字格式 (JSON / XML / CSV):人類可讀、語言中立、無需編譯,但每筆都重複攜帶欄位名、型別模糊(數字精度、bytes 難表達),體積與解析成本高。
- 二進位 schema 格式 (Thrift / Protobuf / Avro):靠外部 schema 定義結構,資料只存值,體積小、解析快,但必須先有 schema 才看得懂 bytes。
- 語言中立:schema 用 IDL 描述,可產生多語言的 stub,跨服務 / 跨語言交換不綁單一 runtime。
⚖️ Tradeoffs
文字格式 — 可讀性換體積與精度
- JSON / XML 自帶結構、debug 友善、瀏覽器與工具鏈原生支援。
- 缺點:欄位名重複攜帶造成膨脹;數字精度不明(JSON 無區分整數/浮點,大整數會被 JS 截斷);二進位資料只能 Base64(再膨脹 33%)。
- 適合對外 API、設定檔、log——可讀性的價值大於效率。
Thrift / Protobuf — 用 field tag 對齊
- schema 給每個欄位一個數字 field tag,encoded bytes 裡存的是 tag + 型別 + 值,不存欄位名。
- 相容性靠 tag 維繫:改欄位名沒差(tag 不變),但 tag 一旦用過就不能改用途。
- Protobuf 用 varint 等緊湊編碼,體積通常比 JSON 小數倍;兩者都需先 compile schema 產 stub。
Avro — writer/reader schema,無 tag
- 最精簡:encoded bytes 裡只有值,連 field tag 都沒有,靠順序對齊。
- 解碼時需同時拿到 writer schema(寫入時用的)與 reader schema(讀取時期望的),由 schema resolution 對齊兩者差異。
- 特別適合大檔案 / 資料倉儲(Hadoop、Kafka + Schema Registry):schema 只在檔頭存一次,動態產生的 schema 也好處理。
效率與體積對比
- 體積:Avro ≈ Protobuf < Thrift < 二進位 XML << JSON < XML(粗略順序,視資料而定)。
- CPU:二進位格式編解碼遠快於 JSON 字串解析。
- 代價:二進位格式不可直接肉眼讀,debug 需工具;schema 成為必須管理的 artifact。
🔑 Takeaways
- 對外 / 可讀性優先用 JSON;對內 / 效率與體積優先用二進位 schema 格式。
- 三種二進位格式的核心差異是「欄位怎麼對齊」:Thrift / Protobuf 用 field tag,Avro 用 writer/reader schema 無 tag。
- 二進位格式的代價是 schema 變成必須管理的 artifact,且 bytes 不可肉眼讀。
- 體積與效率的省,幾乎都來自「不重複攜帶欄位名」——這也是文字格式膨脹的根源。
2026-07-01
- Q: Protobuf 用 tag、Avro 不用 tag,差在哪?為什麼 Avro 還能演化?
- A: Protobuf 靠「tag 不變」來辨識欄位,所以你能自由增刪欄位、改名,只要 tag 不重用。Avro 沒 tag、純靠順序,乍看很脆,但它的 schema resolution 會在解碼時用欄位名把 writer schema 與 reader schema 對齊:reader 有而 writer 沒有的欄位用預設值補,writer 有而 reader 沒有的欄位直接略過。代價是你必須知道 writer schema(所以要 Schema Registry 或檔頭內嵌),這也正是 Avro 在「大量同 schema 記錄」場景最省的原因——schema 不必每筆攜帶。