
JSON ยังจำเป็นไหม? รู้จัก 4 รูปแบบข้อมูลและทดลองด้วย Go
JSON ยังเป็นตัวเลือกที่ดีเมื่อความเข้าใจง่ายและการเชื่อมต่อสำคัญ แต่ถ้าเวลาส่วนใหญ่ของระบบหมดกับการแปลงข้อมูลหรือส่ง payload ขนาดใหญ่ รูปแบบ binary ก็น่าทดลองครับ ผมทำ Workshop Go ให้ลองด้วยข้อมูลสินค้าภาษาไทยชุดเดียวกัน จะได้เห็นทั้งขนาด เวลา และต้นทุนการนำมาใช้
บทความต้นทางบอกอะไร และเราอ่านได้แค่ไหน
ส่วนเปิดอ่านได้ของบทความ Medium โดย The Thread Whisperer ชี้ว่าต้นทุนการแปลงข้อมูลเป็นข้อความอาจเป็นคอขวด และเสนอ Protobuf, MessagePack, Avro กับ FlatBuffers สำหรับการสื่อสารระหว่างระบบ โดยยังใช้ JSON ที่ขอบระบบซึ่งคนหรือ client ภายนอกต้องอ่าน
ชื่อบทความกล่าวถึง throughput เพิ่ม 5 เท่า แต่ส่วนรายละเอียดอยู่หลังระบบสมาชิก เราจึงตรวจชุดข้อมูล โค้ด เครื่อง และวิธีวัดของผู้เขียนไม่ได้ และไม่ได้ยืนยันตัวเลขนั้น การรับคำขอได้มากขึ้น 5 เท่าก็ไม่เท่ากับ latency ทุกคำขอลดลง 5 เท่า เนื้อหาด้านล่างขยายจากเอกสารทางการและตัวอย่างที่เราสร้างใหม่
รู้จักตัวเลือกก่อนเปลี่ยนระบบ
| รูปแบบ | จุดเด่น | ต้นทุนที่ต้องรับ |
|---|---|---|
| JSON | อ่านได้โดยตรง เครื่องมือรองรับกว้าง | มีชื่อฟิลด์และข้อความซ้ำ ต้อง parse |
| MessagePack | เก็บชนิดข้อมูลแบบ binary ใช้กับ struct ได้โดยไม่ต้องสร้าง schema แยก | ต้องมี decoder ทั้งสองฝั่ง ชื่อฟิลด์ยังอยู่เมื่อใช้ map encoding |
| Protobuf | สัญญาข้อมูลชัดเจน ใช้หมายเลขฟิลด์และ generated code | ต้องดูแล schema/compiler และกฎเปลี่ยนฟิลด์ |
| Avro | ใช้ writer/reader schema ช่วยจัดการวิวัฒนาการข้อมูล | ผู้อ่านต้องรู้ schema ที่ถูกต้อง รวมถึงรูปแบบ envelope ที่ใช้ส่ง |
| FlatBuffers | อ่านบางฟิลด์ผ่าน buffer ได้โดยไม่สร้าง object ทั้งชุด | การสร้าง buffer และ full decode มีต้นทุน ต้องดูแลอายุของ buffer |
Protobuf เหมาะเมื่อทีมควบคุมสัญญาข้อมูลร่วมกันได้ การใช้ Protobuf ไม่ได้บังคับว่าต้องใช้ gRPC เราทดลองส่ง bytes ผ่าน HTTP ปกติ ส่วน MessagePack เหมาะกับการเริ่มทดลองจาก struct ที่มีอยู่ แต่ถ้า producer เปลี่ยนความหมายของฟิลด์โดยไม่ตกลงกัน ปัญหา compatibility ก็ยังเกิดได้
Avro raw binary ใน Workshop ใช้ schema เดียวที่ทั้งสองฝั่งรู้ล่วงหน้า เราไม่ได้ส่ง Object Container File หรือเพิ่ม Schema Registry จึงต้องแยกต้นทุนของระบบเหล่านั้นหากจะนำไปใช้จริง ส่วน FlatBuffers น่าสนใจเมื่ออ่านบางฟิลด์บ่อย การอ่านราคาเดียวจึงถูกวัดแยกจากการสร้างสินค้าทั้งชุดกลับเป็น Go struct
คำถามที่ช่วยเลือกได้มากกว่า “อะไรเร็วที่สุด”
เริ่มจาก profile ว่าช้าในฐานข้อมูล เครือข่าย หรือ encode/decode ถ้า serialization กินเวลาเพียงเล็กน้อย การเปลี่ยน format อาจเพิ่มภาระมากกว่าผลที่ได้ ลอง pagination และ gzip กับ JSON เป็น baseline ด้วย แล้วพิจารณาว่าทั้งสองฝั่งอัปเดต library พร้อมกันได้หรือไม่ มี proxy/cache/observability ที่อ่าน binary ได้หรือยัง
สำหรับข้อมูลการเงินในตัวอย่าง ผมเก็บราคาเป็น int64 หน่วยย่อยเพื่อไม่ให้การเปรียบเทียบติดปัญหาทศนิยม ทั้งห้ารูปแบบต้องคืนชื่อภาษาไทย สถานะ stock และ tags ครบก่อนจึงนำความเร็วมาเทียบกัน
ลงมือทำใน 6 บท
เริ่ม Workshop Go API Serialization หรือ ดาวน์โหลดโปรเจกต์เต็ม
- สร้าง JSON baseline และข้อมูลไทย 1, 100 และ 1,000 รายการ
- เปลี่ยนเป็น MessagePack และเทียบขนาดก่อน/หลัง gzip
- สร้าง Protobuf schema พร้อม generated code
- ใช้ Avro schema โดยไม่คิดเวลา parse schema ใน encode/decode
- แยก FlatBuffers direct access ออกจาก full decode
- วัด HTTP throughput, p50/p95 และอ่านค่ากลางจาก 5 รอบ
ผลทดลองของเราอยู่ใน บทที่ 6 พร้อมไฟล์ดิบและสภาพแวดล้อม ไม่มีเงื่อนไขว่ารูปแบบใหม่ต้องชนะ JSON ถ้าผลไม่คุ้มต้นทุน การเก็บ JSON ไว้ก็เป็นข้อสรุปที่มีประโยชน์ครับ