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

encoding/gob: định dạng nhị phân của riêng Go, và cái bẫy khi chỉ gửi một giá trị

gob là serialization gọn của Go — tự mô tả kiểu nên không cần schema ngoài như Protobuf. Nhưng nó có một đặc tính dễ gây bất ngờ: gửi một giá trị lẻ thì gob còn LỚN hơn JSON vì gói kèm mô tả kiểu. Bài này đo thật: 1 giá trị gob 443 byte vs JSON 398, nhưng stream 1000 giá trị thì gob chỉ 215 byte/giá trị (~nửa JSON); và Encoder tái dùng mã hóa chỉ 1 lần cấp phát, nhanh hơn JSON.

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

DNS resolution: chuyến đi mạng ẩn xảy ra trước cả bắt tay TCP

Trước khi net.Dial kịp bắt tay TCP, có một bước vô hình phải xong: phân giải tên miền thành IP. Bài này chạy thật trong Go: DNS cold tốn 6,97ms (có lúc vọt lên 140ms), cached chỉ 794µs (~9 lần nhanh hơn), còn /etc/hosts thì 1µs không đi mạng. Và httptrace tách được DNS chiếm bao nhiêu trong một request — vì sao DNS chậm/timeout là nguyên nhân 'app chậm' hay bị bỏ sót.

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

Connection pool: vì sao pool mặc định 2 làm bạn mở 546 kết nối cho 2000 request

Keep-alive tái dùng một kết nối cho các request tuần tự, nhưng khi nhiều request chạy song song, mỗi luồng cần một kết nối cùng lúc — đó là việc của connection pool. Bài này chạy thật trong Go: 2000 request song song 50 luồng với pool mặc định (2) mở tới 546 kết nối vì churn, còn pool đủ lớn chỉ mở 50 và nhanh hơn. Giải thích cách chọn kích cỡ pool đúng.

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

Trạng thái socket TCP: đọc /proc/net/tcp để phân biệt TIME_WAIT bình thường với CLOSE_WAIT là bug

Một kết nối TCP đi qua nhiều trạng thái, và hai trong số đó hay gây sự cố: TIME_WAIT và CLOSE_WAIT. Bài này chạy thật trong Go đọc /proc/net/tcp: mở 3000 kết nối ngắn để lại 3091 TIME_WAIT (ăn cổng cục bộ), còn server quên gọi Close để lại đúng 200 CLOSE_WAIT (rò rỉ fd). Giải thích vì sao TIME_WAIT là bình thường còn CLOSE_WAIT tăng không giảm luôn là bug.

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

Window function: tính theo nhóm mà vẫn giữ từng dòng — và nhanh hơn self-join 124 lần

GROUP BY gộp nhóm lại làm mất chi tiết từng dòng. Window function tính toán theo nhóm (xếp hạng, tổng luỹ kế, so kỳ trước) mà VẪN giữ mọi dòng — thứ mà nhiều người viết bằng self-join phức tạp và chậm. Bài này đo thật trong PostgreSQL: ROW_NUMBER/RANK/DENSE_RANK khác nhau khi có tie, running total + LAG, và một benchmark đắt giá — window làm running total trên 30.000 dòng mất 15.9ms, còn self-join tương quan chỉ 3.000 dòng đã mất 1971ms.