doctrine默认不自动加索引,必须显式声明#[orm\index]于实体类顶部;单列索引需写成['field']格式,建议显式命名;复合索引注意字段顺序,条件索引需数据库版本支持;函数索引需借助第三方bundle或手写迁移。

Doctrine 默认不自动加索引,必须显式声明;不写 #[ORM\Index],迁移脚本里就不会有 CREATE INDEX。
单列索引怎么加才有效
索引必须写在实体类顶部,紧挨着 #[ORM\Entity] 和 #[ORM\Table],不能放在属性或方法上。Doctrine 只认类作用域的元数据。
-
columns必须是数组:哪怕只索引一个字段,也得写成['email'],写成'email'会报错或静默忽略 -
name参数建议显式指定:比如name: 'idx_email',否则 Doctrine 自动生成的名字(如IDX_123ABC)难排查、难删除 - MySQL 中
UNIQUE约束自带索引,如果已有#[ORM\UniqueConstraint(columns: ['email'])],通常不用再加同字段的普通索引
复合索引和条件索引要注意什么
复合索引的字段顺序直接影响查询性能——WHERE 条件中越靠前的字段,越容易被 B-tree 利用。条件索引(Partial Index)只在 PostgreSQL 和 MySQL 8.0+ 支持,且生成的 SQL 是 CREATE INDEX ... WHERE 形式。
- 复合索引示例:
#[ORM\Index(name: 'idx_status_created', columns: ['status', 'created_at'])],status应该是高频等值过滤字段 - 条件索引需用
options: ['where' => "status = 'active'"],字符串值必须单引号,SQL 关键字大写更稳妥 - MySQL 5.7 不支持
WHERE子句的索引,这种写法在 diff 时不会报错,但 migrate 会失败,务必确认数据库版本
函数索引和高级索引怎么处理
Doctrine 原生注解不支持 lower(email)、jsonb_path_ops 或 USING GIN 这类 PostgreSQL 特性。硬写 SQL 迁移又难维护、易出错。
- 推荐使用
intaro/custom-index-bundle:它提供#[CustomIndex]Attribute,支持函数表达式、操作符类、索引类型等 - 安装后只需在实体上写:
#[CustomIndex(name: 'idx_lower_email', expression: 'lower(email)')],然后跑doctrine:migrations:diff即可生成对应 SQL - 注意:该 Bundle 目前只适配 PostgreSQL;若用 MySQL,函数索引只能手写迁移,并确保所有环境执行顺序一致
多库环境下索引怎么同步
每个 entity_manager 是独立的迁移上下文。你在 Customer 实体上加了索引,但没指定 --em=customer,doctrine:migrations:diff 就不会扫描它。
- 生成迁移时必须带上
--em=xxx参数,例如:php bin/console doctrine:migrations:diff --em=customer - 检查迁移文件内容是否含目标索引语句,别只看命令是否成功——Doctrine 有时会跳过“无变更”判断,导致索引漏建
- 不同库的索引命名空间不隔离,
idx_email在default和customer里会冲突,建议按库前缀命名,如idx_customer_email
最常被忽略的是:索引定义写了,但没重新运行 doctrine:migrations:diff;或者跑了 diff 却没检查生成的迁移文件里有没有 CREATE INDEX。Doctrine 不会警告你“这个索引没生效”,它只会安静地跳过。











