Có 2 cách khai báo index trong Django model:
class Post(models.Model):
slug = models.SlugField(unique=True) # tạo index unique tự động
author = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True)
created_at = models.DateTimeField()
class Meta:
indexes = [
models.Index(fields=['-created_at']), # single column
models.Index(fields=['author', '-created_at']), # composite
models.Index(
fields=['title'], name='post_title_published_idx',
condition=Q(status='published'), # partial index (Postgres)
),
]Đặt index ở cột thường xuyên xuất hiện trong WHERE, JOIN, ORDER BY. Composite index đi từ trái sang phải — (author, created_at) phục vụ tốt query WHERE author=? ORDER BY created_at, nhưng không giúp gì cho query chỉ WHERE created_at=?.
Index không miễn phí — mỗi index làm INSERT/UPDATE/DELETE chậm hơn vì DB phải maintain B-tree. Bảng ghi heavy + nhiều index = bottleneck. Trước khi thêm index mới, chạy EXPLAIN ANALYZE xem planner có thực sự chọn index hay vẫn seq scan; nếu không dùng tới thì bỏ luôn, đừng để cho có.
Two ways to declare them:
class Post(models.Model):
slug = models.SlugField(unique=True) # auto unique index
author = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True)
created_at = models.DateTimeField()
class Meta:
indexes = [
models.Index(fields=['-created_at']), # single column
models.Index(fields=['author', '-created_at']), # composite
models.Index(
fields=['title'], name='post_title_published_idx',
condition=Q(status='published'), # partial index (Postgres)
),
]Index columns that often appear in WHERE, JOIN, ORDER BY. Composite indexes work left-to-right — (author, created_at) serves WHERE author=? ORDER BY created_at, but does not help WHERE created_at=? alone.
Pitfall: Indexes are not free — each one slows INSERT/UPDATE/DELETE because the DB maintains the B-tree. A write-heavy table + many indexes = bottleneck. Measure first: EXPLAIN ANALYZE to confirm the planner actually picks the index instead of a seq scan; if not, drop it.