15 tháng 3, 2026 · 11 phút đọc

Chiến lược indexing PostgreSQL cho ứng dụng web quy mô vừa

Khi nào tạo index, loại index nào phù hợp, và cách phân tích query execution plan để không lãng phí tài nguyên.

Chiến lược indexing PostgreSQL cho ứng dụng web quy mô vừa

Index là công cụ mạnh nhất để tăng tốc query PostgreSQL — nhưng cũng là nguồn gốc của nhiều vấn đề hiệu năng nếu dùng sai. Mỗi index cần không gian lưu trữ và làm chậm write operations. Hiểu khi nào nên và không nên tạo index là kỹ năng quan trọng.

EXPLAIN ANALYZE: công cụ không thể thiếu

Trước khi tạo bất kỳ index nào, chạy EXPLAIN (ANALYZE, BUFFERS) trước query. Output cho bạn biết query planner chọn plan gì, bao nhiêu rows được scan, và thời gian thực tế. Seq Scan trên table nhỏ là bình thường — đừng vội tạo index.

Chú ý đến 'rows=X' estimated vs actual: nếu chênh lệch lớn, table statistics đã cũ. Chạy ANALYZE để refresh. Planner đưa ra quyết định dựa trên statistics — statistics không chính xác dẫn đến bad plans.

Database server infrastructure
Query execution plan analysis là bước đầu tiên của mọi optimization

B-tree Index: mặc định và versatile nhất

B-tree index (mặc định khi CREATE INDEX) phù hợp với equality và range queries: =, <, >, BETWEEN, LIKE 'prefix%'. Không phù hợp với LIKE '%suffix' hoặc full-text search.

Multi-column index: thứ tự columns rất quan trọng. Index (a, b, c) hỗ trợ queries trên a, a+b, và a+b+c — nhưng không hỗ trợ b hoặc c riêng lẻ. Đặt selective column đầu tiên.

GIN Index cho array và JSONB

GIN (Generalized Inverted Index) được thiết kế cho dữ liệu composite như arrays, JSONB, và full-text tsvector. Nếu bạn query vào JSONB fields thường xuyên, GIN index với jsonb_path_ops là lựa chọn tốt.

GIN index tốn nhiều thời gian build và space hơn B-tree, nhưng query performance cho containment operators (@>, <@) vượt trội so với sequential scan.

Partial Index: index thông minh hơn

Partial index chỉ index rows thỏa mãn một điều kiện: CREATE INDEX idx_active_users ON users(email) WHERE active = true. Nhỏ hơn full index, hiệu quả hơn với queries luôn có cùng filter.

Index Bloat và Maintenance

PostgreSQL dùng MVCC — rows cũ không bị xóa ngay. Sau nhiều UPDATEs và DELETEs, index bị bloat. Dùng REINDEX CONCURRENTLY để rebuild index mà không lock table. Cài pg_stat_user_indexes để monitor index usage và phát hiện unused indexes.