ducdev
Database / Bài viết

Database Indexing — Hiểu Đúng B-Tree, Hash Và Partial Index

Index không phải là "thêm vào cho nhanh". Mỗi index có chi phí riêng và phù hợp với từng loại query. Hiểu B-Tree, Hash index giúp bạn quyết định đúng khi nào và index column nào.

a
admin
20/03/2026 · 3 phút đọc · 0 lượt xem
Chia sẻ

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 BY
  • IS 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".

#database #PostgreSQL #Indexing #Performance #SQL
a
Tác giả
admin

Lập trình viên, yêu thích chia sẻ kiến thức về công nghệ và phát triển phần mềm.

Bình luận

Chưa có bình luận. Hãy là người đầu tiên!

Để lại bình luận

Bình luận sẽ được duyệt trước khi hiển thị.