Cơ sở dữ liệu 03/09/2026 8 phút

Hash join 78ms hay 223ms cho cùng một câu? Con số 'Batches' trong EXPLAIN nói tất cả

Join hai bảng lớn: hash join 78ms (bảng băm vừa RAM, Batches=1), nhanh gần 3 lần nested loop. Nhưng work_mem nhỏ làm bảng băm tràn 16 batch ra đĩa và chậm hẳn. Tôi suýt kết luận 'hash join nhanh' vô điều kiện — cho tới khi hạ work_mem và thấy Batches nhảy lên 16. Đọc Batches, không chỉ thời gian tổng.

Cơ sở dữ liệu 03/09/2026 9 phút

Merge join: 77ms khi dữ liệu đã sắp, 160ms khi phải tự sắp — và vì sao đừng vội chê nó thua hash

Merge join trên khóa đã sắp: 77ms không cần sort; nhưng trên cột chưa sắp phải thêm Sort tràn đĩa, 160ms (hash chỉ 78ms). Tôi suýt kết luận 'merge dở hơn hash' — cho tới khi thấy mình đo nó ở đúng điều kiện bất lợi. Đổi lại nó giữ nguyên thứ tự: ORDER BY LIMIT nhanh 0,03ms so với 72ms của hash.

Cơ sở dữ liệu 03/09/2026 8 phút

ORDER BY ... LIMIT 10 không sắp cả triệu hàng như bạn tưởng — nó chỉ giữ 10, tốn 25kB

ORDER BY trên 1 triệu hàng: work_mem nhỏ thì external merge tràn đĩa 208ms, lớn thì quicksort RAM 162ms. Nhưng ORDER BY LIMIT 10 dùng top-N heapsort chỉ 25kB, 53ms — không sắp hết. Đừng đoán thuật toán từ câu SQL; EXPLAIN cho biết cơ sở dữ liệu thật sự sắp thế nào, và một index có thể bỏ luôn bước sắp.

Cơ sở dữ liệu 03/09/2026 8 phút

Cùng 2 triệu hàng, GROUP BY này tốn 24kB còn kia tràn 62MB ra đĩa — khác nhau ở đâu?

GROUP BY 100 nhóm trên 2 triệu hàng dùng HashAggregate chỉ 24kB; nhưng GROUP BY 2 triệu nhóm phân biệt làm nó tràn 62MB ra đĩa. Tôi suýt kết luận 'GROUP BY nhẹ RAM bất kể bảng lớn'. Bộ nhớ tính theo SỐ NHÓM phân biệt, không phải số hàng — và đó cũng là cái bẫy của COUNT(DISTINCT).

Cơ sở dữ liệu 03/09/2026 8 phút

Planner đoán WHERE v<10 khớp 333350 hàng, thực tế 10000 — không phải nó dở, nó đang bị bịt mắt

Bảng vừa nạp 1 triệu hàng chưa ANALYZE: planner ước lượng WHERE v<10 là 333350 hàng, thực tế 10000 — lệch 33 lần. Tôi suýt trách planner ngu. Chạy ANALYZE, ước lượng về 9872, khớp. Thống kê bảng là nguồn của mọi ước lượng, và một planner mù thống kê buộc phải đoán bằng hằng số mặc định.

Cơ sở dữ liệu 03/09/2026 8 phút

101 truy vấn, câu nào cũng dưới 0,2ms — vì sao đo cục bộ giấu mất thảm họa N+1 qua mạng

Lấy 100 bài rồi mỗi bài một truy vấn bình luận: 101 truy vấn, mỗi câu <0,2ms, đo cục bộ chỉ chậm 3 lần một JOIN nên tôi suýt xem thường. Nhưng qua mạng, 100 round-trip tuần tự làm N+1 chậm 10-100 lần. Số round-trip mới là đại lượng quyết định, và pg_stat_statements với calls=100 là dấu vết để nhận ra.