复合索引在Django 4.2中必须显式定义并执行makemigrations和migrate才能生效,仅添加Index注解不迁移则查询仍慢;fields顺序影响查询覆盖范围,降序需用'-field'字符串写法,函数索引须用expressions参数配合Func对象。

复合索引在Django 4.2中必须显式定义,Index注解不会自动生效
直接写在模型 Meta.indexes 里的 Index 注解,只有执行 python manage.py makemigrations 后生成迁移文件并运行 migrate,数据库才会真正创建索引。光靠注解不迁移,查询照样慢——这是最常被忽略的前提。
常见错误现象:SELECT ... WHERE status='active' AND created_at > '2023-01-01' 查询仍走全表扫描,而你已经加了 Index(fields=['status', 'created_at'])。
- 确认迁移是否已应用:
python manage.py showmigrations your_app_name,检查对应迁移状态是否为 [X] -
fields顺序很重要:WHERE 条件中越靠前的字段,越应放在fields列表靠前位置(例如['status', 'created_at']支持status=...单条件,也支持两者组合;但['created_at', 'status']对纯status=...查询无效) - 避免冗余索引:如果已有
['status', 'created_at'],再加['status']单字段索引通常没必要,反而增加写入开销
用 Index 实现降序索引需显式指定 desc,不能靠 -field
Django 的 ordering = ['-created_at'] 不会改变索引方向;要让 ORDER BY created_at DESC 走索引,必须在 Index 中用 fields=['created_at', '-updated_at'] 这种写法——注意是字符串带负号,不是字段名加负号。
典型场景:分页查最新 20 条记录,SQL 含 ORDER BY created_at DESC LIMIT 20,若没建降序索引,即使有 created_at 普通索引,也可能触发 filesort。
立即学习“Python免费学习笔记(深入)”;
- 正确写法:
Index(fields=['status', '-created_at'])→ 生成(status, created_at DESC)复合索引 - 错误写法:
Index(fields=['status', 'created_at'], ordering=['ASC', 'DESC'])——ordering参数在 Django 4.2 中已被移除,会报TypeError - PostgreSQL 和 MySQL 8.0+ 支持降序索引;SQLite 不支持,会忽略
-,仅建升序
函数索引(如 Lower('email'))需配合 Index 的 expressions 参数
想加速 email__iexact 或 filter(email__icontains='@gmail.com'),不能只靠字段本身索引。Django 4.2 允许用 expressions 定义函数索引,但必须确保数据库后端支持(PostgreSQL 推荐,MySQL 限制多)。
例如对邮箱做小写统一索引,避免每次查询都调 LOWER() 函数:
from django.db import models
from django.db.models import Func
<p>class User(models.Model):
email = models.EmailField()</p><pre class='brush:python;toolbar:false;'>class Meta:
indexes = [
models.Index(
fields=['email'],
name='user_email_lower_idx',
# 注意:此处用 expressions,不是 fields
expressions=[Func('email', function='LOWER')],
),
]</pre>-
expressions必须是Func或F等表达式对象,字符串如'LOWER(email)'会失败 - MySQL 对函数索引要求严格:必须是“确定性”函数,且字段类型需匹配(比如
VARCHAR才能建LOWER()索引) - 迁移后验证:连接数据库执行
\d+ your_table_name(PostgreSQL)或SHOW INDEX FROM your_table(MySQL),确认索引含 expression 字段
联合唯一约束和查询优化可共用同一 Index,但语义不同
UniqueConstraint 和 Index 都能加速等值查询,但前者强制数据完整性,后者只优化查询。Django 允许用 Index 替代部分 UniqueConstraint 场景,减少索引数量。
例如用户需按 (org_id, slug) 唯一,且高频查 org_id=123 AND slug='abc':
- 用
UniqueConstraint(fields=['org_id', 'slug'])→ 创建唯一索引 + 数据校验 - 用
Index(fields=['org_id', 'slug'], name='org_slug_idx')→ 仅加速查询,不阻止重复插入 - 若业务层已保证唯一性(如 slug 生成逻辑强控),可省掉
UniqueConstraint,单靠Index降低索引维护成本 - 但要注意:缺失
UniqueConstraint后,DB 层不再拦截重复数据,应用层必须兜底
索引不是越多越好,每个额外索引都会拖慢 INSERT/UPDATE,尤其在高写入场景下,fields 顺序、是否降序、是否函数表达式,这些细节一旦错配,就等于白建。



















