优先用foreignid()处理外键字段(如user_id),它自动建索引并声明约束;普通查询字段(如status、created_at)用index();unique()用于唯一性校验,不可用index()替代。

迁移中用 index() 还是 foreignId()?
加索引不是加了就完事,得看字段用途。如果只是普通查询字段(比如 status、created_at),直接在迁移里用 index();如果是外键字段(比如 user_id),优先用 foreignId() —— 它会自动加索引,还能声明约束关系。
常见错误:手动写 $table->unsignedBigInteger('user_id')->index(),结果忘了加约束,或者漏掉 index() 导致关联查询慢。Laravel 8+ 推荐写法:
$table->foreignId('user_id')->constrained()->onDelete('cascade');
这行代码等价于建外键 + 加索引 + 级联删除,比拆成两步更安全。
unique() 和 index() 能不能一起用?
能,但要注意顺序和语义。比如邮箱唯一性校验,应该用 unique(),它内部会建唯一索引;如果只用 index(),重复插入不会报错,只加速查询。
实操建议:
-
$table->string('email')->unique()→ 正确:强制唯一 + 自动建唯一索引 -
$table->string('email')->index()→ 错误:不阻止重复,且索引类型是普通索引,无法替代唯一约束 - 复合唯一索引用
->unique(['user_id', 'product_id']),别手写INDEX语句
注意:MySQL 中,unique() 索引允许一个 NULL 值;如果字段允许为空又想“逻辑唯一”,得靠应用层或触发器补位,Laravel 迁移不处理这个边界。
什么时候必须用 fullText() 或自定义索引?
默认的 B-tree 索引对 LIKE '%xxx%' 或全文搜索无效。如果要做模糊前缀搜索(如 WHERE name LIKE 'abc%'),B-tree 还能用;但要是 LIKE '%abc%' 或中文分词搜索,就得上 fullText()。
Laravel 9+ 支持:
$table->text('content')->fullText();
但注意三点:
- 仅 MySQL/PostgreSQL 支持,SQLite 不行
- MySQL 要求引擎是 InnoDB(5.6+)或 MyISAM,且字段类型为
CHAR/VARCHAR/TEXT - 执行
DB::statement("SELECT * FROM posts WHERE MATCH(content) AGAINST('laravel' IN NATURAL LANGUAGE MODE)")才生效,Eloquent 不自动翻译
迁移回滚后索引还在吗?
不在。Laravel 迁移的 down() 方法默认只删表,不删索引 —— 如果你没显式写 dropIndex(),那回滚后索引会残留,下次迁移可能报 “index already exists” 错误。
正确做法:
- 建索引时记下名称,比如
$table->index('email', 'users_email_index') - 在
down()里对应删:$table->dropIndex('users_email_index') - 更省事的是用字段名推导:
$table->dropIndex(['email']),Laravel 会自动拼出标准名(如users_email_index)
容易被忽略的是联合索引命名规则:Laravel 默认按字段顺序拼接,['user_id', 'status'] 的索引名是 users_user_id_status_index,手抖写成 ['status', 'user_id'] 就对不上。











