Q

Quản trị viên

Là một lập trình viên yêu công nghệ, thích viết blog để chia sẻ kiến thức, kinh nghiệm lập trình và những điều thú vị trong thế giới tech.

2312 bài viết · Website
Lập trình 22/09/2026 6 phút

MVCC và VACUUM: vì sao UPDATE làm bảng phình 9 lần dù số dòng không đổi

PostgreSQL không sửa dòng tại chỗ — mỗi UPDATE tạo một phiên bản mới và để lại 'xác chết' (dead tuple). Bài này đo thật: một bảng 100.000 dòng sau 8 lần UPDATE toàn bộ phình từ 3.5MB lên 31MB với 799.810 dead tuple, dù vẫn đúng 100.000 dòng sống. VACUUM dọn dead tuple (dead về 0) nhưng không trả đĩa; chỉ VACUUM FULL trả bảng về 3.5MB — nhưng khoá cả đọc. Hiểu MVCC để không bị bloat bất ngờ.

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

pprof: đừng đoán hàm nào chậm, đo — CPU profile và heap profile tìm đúng thủ phạm

Tracing chỉ ra chặng nào chậm; nhưng trong một chặng, HÀM nào ngốn CPU hay giữ RAM? Đừng đoán — pprof của Go đo trực tiếp. Bài này chạy thật runtime/pprof: CPU profile chỉ ra main.hotHash chiếm 100% CPU (qua sha256), heap profile chỉ ra main.allocBig giữ đúng 197.76MB còn allocSmall biến mất vì đã bị GC. Kèm cách đọc hai cột flat vs cum mà nhiều người nhầm.

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

Index B-tree: vì sao tạo index rồi vẫn Seq Scan — bẫy non-sargable và cách viết query cho đúng

Bạn tạo index nhưng query vẫn chậm và EXPLAIN vẫn báo Seq Scan? Vì predicate không sargable — chỉ cần bọc cột trong một hàm là index nằm im vô dụng. Bài này đo thật trên bảng 1 triệu dòng: WHERE email=x dùng index (0.045ms), nhưng lower(email)=x quét cả bảng (54.857ms); age+1=31 chậm 12ms còn age=30 chỉ 1ms; và một bất ngờ — LIKE 'x%' với index thường vẫn Seq Scan, phải dùng text_pattern_ops.

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

Log correlation: gắn trace_id vào context để dựng lại hành trình một request giữa hàng nghìn dòng log

Có log JSON đẹp rồi, nhưng giữa 150 dòng log của 50 request chạy song song, đâu là những dòng của riêng một request? Nếu không có ID chung, chúng đan xen theo lịch chạy goroutine và bạn không tách nổi. Bài này đo thật trong Go: một slog.Handler tùy biến tự gắn trace_id lấy từ context, để mỗi request mang một ID xuyên suốt; rồi một câu jq lọc đúng 3 bước của request trace-0007 giữa 150 dòng — và mọi trace_id đều xuất hiện đúng 3 lần, không sót không lẫn.

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

N+1 query: ORM giấu 101 lần đấm xuống DB sau một vòng lặp — và cách gộp về 1 query

Bạn viết một vòng lặp vô hại trong ORM, và nó lặng lẽ bắn 101 query xuống database. Đó là N+1 — cái bẫy hiệu năng phổ biến nhất của backend. Bài này đo thật trong PostgreSQL: lấy 100 tác giả rồi lặp lấy sách từng người. Trên cùng một connection, N+1 chậm 2.7 lần; nhưng khi mỗi query là một round-trip thật (như app gọi qua mạng), N+1 mất 1110ms so với 17ms của một JOIN — chậm 64 lần. Chi phí không ở database, mà ở 101 lần đi-về cộng dồn.

Cơ sở dữ liệu 22/09/2026 6 phút

Cập nhật và xoá hàng loạt trong PostgreSQL: vì sao chia lô chậm hơn mà vẫn nên làm

Một UPDATE/DELETE khổng lồ nhanh hơn về wall-clock nhưng khoá mọi dòng suốt cả giao dịch, dồn hàng trăm MB WAL một cục và phình bảng gần gấp đôi. Đo thật: UPDATE 2 triệu dòng sinh 722 MB WAL và 2 triệu dead tuple; chia lô kèm VACUUM giữ bloat thấp. PostgreSQL 16.