Query plan PostgreSQL

Đọc EXPLAIN từ con số 0 tới chẩn đoán plan sai, qua hơn 60 ví dụ trên một database e-commerce giả lập kiểu project laravel-livewire (2 triệu đơn, 5 triệu dòng hàng). Nên đọc theo thứ tự 1 → 5. Mọi plan là output thật trên PostgreSQL 18.4; SQL của Eloquent lấy từ Laravel 12.64.

Bài 1 · Nền tảng

Đọc query plan từ con số 0 — cây node, cost, rows, loops, buffers

Đọc từ trong ra ngoài; tính tay cost và rows khớp từng số; loops và số lẻ của PG 18; cùng plan 134 ms → 19 ms theo cache; DELETE có 95% thời gian nằm ngoài cây plan.

Bài 2 · Scan

Scan node — Seq, Index, Index Only, Bitmap: đọc từng loại và vì sao planner chọn

foreignId()->constrained() không tạo index: DELETE 20 đơn mất 10 giây; whereDate, whereYear, JSON, LIKE làm index vô dụng; VACUUM bỏ qua dọn index; skip scan PG 18.

Bài 3 · Join

Join node — Nested Loop, Hash Join, Merge Join, Memoize, semi/anti join

Batches và "originally"; Memoize 984 hit / 50 miss; NOT IN 10 phút chưa xong so với NOT EXISTS 1,6 giây; LEFT JOIN thành INNER JOIN; ước lệch 120 lần mà Nested Loop vẫn thắng.

Bài 4 · Sort, Limit…

Sort, Aggregate, Limit, CTE, SubPlan, Window, Partition

Bẫy ORDER BY + LIMIT bỏ qua 1,3 triệu dòng; hai plan cost ngang nhau mà 10 s so với 2,3 s; OFFSET và keyset; withCount; ba dấu hiệu partition pruning.

Bài 5 · Chẩn đoán

Khi plan sai — ước lượng lệch, thống kê, generic plan, cost model và vận hành

CREATE STATISTICS, thống kê biểu thức, n_distinct, thống kê cũ sau lô nhập; generic plan lần thứ 6 (Laravel không bị); random_page_cost; JIT; auto_explain; runbook 3 giờ sáng.