Lập trình 22/09/2026 5 phút

Nén + serialize: sự thật bất ngờ khi bạn gzip một JSON và một Protobuf

Protobuf nhỏ hơn JSON ~2 lần — ai cũng biết. Nhưng nếu bạn vốn đã gzip dữ liệu trước khi gửi thì sao? Bài này đo thật 1000 đơn hàng: JSON nén gzip xuống 11,6 lần, Protobuf chỉ 5,1 lần (vì đã gọn, ít phần lặp). Kết quả bất ngờ: JSON+gzip (33KB) không chỉ nhỏ hơn Protobuf thô (188KB) mà còn nhỏ hơn cả Protobuf+gzip — nén xóa gần hết lợi thế kích thước của Protobuf.

Lập trình 22/09/2026 6 phút

Schema evolution: thêm field mà không làm sập dịch vụ đang chạy phiên bản cũ

Trong hệ phân tán, code cũ và mới chạy song song lúc triển khai — dữ liệu phải đọc chéo được cả hai chiều. Bài này chạy thật bằng Protobuf: code mới ghi 4 field, code cũ đọc vẫn lấy được 2 field và bỏ qua field lạ không crash; code cũ ghi 2 field, code mới đọc field mới nhận default. Và một cái bẫy đo được: đổi kiểu field làm mất dữ liệu âm thầm. Kèm luật an toàn vs nguy hiểm.

Lập trình 22/09/2026 6 phút

Zero-copy serialization: đọc một field trong 0,25 nano giây, không cấp phát

JSON và Protobuf phải giải mã cả bản ghi thành struct trước khi đọc một field — lãng phí khi bạn chỉ cần vài field trong message lớn. Zero-copy (FlatBuffers, Cap'n Proto) đọc thẳng field từ buffer bằng offset, không giải mã, không cấp phát. Bài này đo thật bằng Go: đọc một trường mất 0,25 ns và 0 lần cấp phát, so với 1524 ns và 9 lần khi phải unmarshal cả bản ghi — nhanh hơn khoảng 6000 lần.

Lập trình 22/09/2026 6 phút

Chọn định dạng serialization nào? Tổng kết 12 phần bằng một bảng đo thật

Khép lại loạt Serialization: điểm lại JSON, Protobuf, gob, MessagePack, CBOR, zero-copy — mỗi cái mạnh ở đâu — rồi chạy thật cả năm trên cùng một đơn hàng để so kích thước, tốc độ mã hóa/giải mã và cấp phát. Kết quả đo được: Protobuf nhỏ nhất (205 byte) và giải mã nhanh nhất, JSON dễ đọc nhất nhưng giải mã chậm nhất (2793 ns). Kèm cây quyết định và lời khuyên khi nào cứ JSON là đủ.

Lập trình 22/09/2026 6 phút

TCP bắt tay 3 bước nhìn từ code: vì sao mỗi kết nối mới tốn một vòng khứ hồi

Mỗi lần net.Dial hay mở một kết nối HTTP, TCP phải bắt tay 3 bước SYN/SYN-ACK/ACK trước khi gửi được byte dữ liệu đầu tiên — tốn đúng một RTT. Bài này chạy thật trong Go: đo thời gian bắt tay, so kết nối mới mỗi request với tái dùng một kết nối (nhanh hơn 13 lần), và đọc trạng thái socket từ /proc/net/tcp để thấy LISTEN, ESTABLISHED và đống TIME_WAIT.

Lập trình 22/09/2026 6 phút

TCP vs UDP: đo thật cảnh UDP mất 99,9% gói và vì sao đó không phải lỗi

TCP đảm bảo gói tới đủ và đúng thứ tự; UDP bắn và quên, không đảm bảo gì. Bài này chạy thật trong Go: gửi 200.000 gói — TCP nhận đủ 100% đúng thứ tự, còn UDP khi bị bắn dồn dập vào buffer nhỏ mất tới 99,9%. Nhưng UDP nhanh hơn và header chỉ 8 byte. Giải thích vì sao mỗi giao thức đúng cho việc của nó, không cái nào tệ hơn.