Nhiều developer thêm index theo kiểu "có vẻ cần" mà không hiểu cơ chế hoạt động. Kết quả: đôi khi nhanh hơn, đôi khi không khác gì, và đôi khi còn chậm hơn do overhead.
B-Tree Index — Default Và Phổ Biến Nhất
PostgreSQL dùng B-Tree làm default. Phù hợp với:
- So sánh:
=,<,>,BETWEEN LIKE 'prefix%'(prefix match)ORDER BYIS NULL
-- B-Tree index (default)
CREATE INDEX idx_posts_published_at ON posts(published_at DESC);
-- Query sử dụng index
SELECT * FROM posts
WHERE published_at > '2026-01-01'
ORDER BY published_at DESC
LIMIT 10;
Hash Index — Chỉ Cho Equality
-- Hash index — chỉ dùng cho =, không dùng được cho range
CREATE INDEX idx_posts_slug ON posts USING HASH (slug);
-- Nhanh hơn B-Tree cho exact match
SELECT * FROM posts WHERE slug = 'hello-world';
-- KHÔNG dùng được cho
SELECT * FROM posts WHERE slug LIKE 'hello%'; -- Index không dùng
Composite Index — Thứ Tự Cột Quan Trọng
-- Index trên (status, published_at)
CREATE INDEX idx_posts_status_date ON posts(status, published_at DESC);
-- Query này DÙNG index (match prefix)
SELECT * FROM posts WHERE status = 'published' ORDER BY published_at DESC;
-- Query này KHÔNG dùng index hiệu quả (bỏ cột đầu)
SELECT * FROM posts WHERE published_at > '2026-01-01';
-- Quy tắc: Filter/Sort theo cùng thứ tự với index columns
Partial Index — Index Thông Minh
-- Chỉ index bài published — nhỏ hơn, nhanh hơn
CREATE INDEX idx_published_posts
ON posts(published_at DESC)
WHERE status = 'published';
-- Query cần khớp WHERE condition của index
SELECT * FROM posts
WHERE status = 'published'
ORDER BY published_at DESC;
-- Partial index đặc biệt hữu ích khi chỉ có ~10% rows thỏa điều kiện
Covering Index — Không Cần Đọc Table
-- INCLUDE thêm columns không filter nhưng cần select
CREATE INDEX idx_posts_list
ON posts(status, published_at DESC)
INCLUDE (title, slug, excerpt);
-- Query này đọc HOÀN TOÀN từ index, không đọc table
SELECT title, slug, excerpt
FROM posts
WHERE status = 'published'
ORDER BY published_at DESC
LIMIT 20;
Khi Nào KHÔNG Nên Tạo Index
- Bảng nhỏ (<1000 rows): Sequential scan thường nhanh hơn
- Cột có cardinality thấp:
gender,is_active(chỉ 2-3 giá trị) - Bảng write-heavy: Index làm chậm INSERT/UPDATE/DELETE
- Cột ít được dùng trong WHERE/ORDER BY
EXPLAIN ANALYZE — Verify Index Được Dùng
EXPLAIN (ANALYZE, BUFFERS)
SELECT title, slug FROM posts
WHERE status = 'published'
ORDER BY published_at DESC
LIMIT 20;
-- Tìm kiếm trong output:
-- "Index Scan" hoặc "Index Only Scan" → Index được dùng ✓
-- "Seq Scan" trên bảng lớn → Thiếu index hoặc planner bỏ qua ✗
Mỗi index là một đánh đổi: đọc nhanh hơn, ghi chậm hơn và tốn thêm disk space. Tạo index có chủ đích, dựa trên query thực tế — không phải dự đoán.
Kết Luận
Workflow đúng: chạy app → tìm query chậm với pg_stat_statements hoặc Django Debug Toolbar → EXPLAIN ANALYZE → tạo index phù hợp → verify hiệu quả. Không tạo index "phòng ngừa".
Chưa có bình luận. Hãy là người đầu tiên!