本文目录导读:

- 使用
from()方法(推荐) - 使用
whereRaw或selectRaw(在复杂查询中) - 在 Eloquent 模型中强制索引
- 使用
useIndex()宏(自定义) - 使用
USE INDEX或IGNORE INDEX - 在 JOIN 中强制索引
- 重要注意事项
- 检查是否生效
- 替代方案:使用数据库索引提示
在 Laravel 中,你可以通过 查询构造器 或 Eloquent ORM 使用 from() 方法或原始表达式来强制指定 MySQL 索引(Force Index)。
以下是几种常见的方式:
使用 from() 方法(推荐)
Laravel 的 from() 方法允许你传入一个原始表达式,从而在表名后面附加 FORCE INDEX 或 USE INDEX。
use Illuminate\Support\Facades\DB;
$users = DB::table('users')
->from(DB::raw('users FORCE INDEX (index_name)'))
->where('email', 'like', '%@example.com')
->get();
使用 whereRaw 或 selectRaw(在复杂查询中)
如果需要在子查询或复杂 JOIN 中强制索引,可以在 from 或 join 子句中使用原始表达式。
$users = DB::table('users')
->select('name', 'email')
->from(DB::raw('users FORCE INDEX (idx_email)')) // 强制使用 idx_email 索引
->where('email', '=', 'user@example.com')
->first();
在 Eloquent 模型中强制索引
Eloquent 模型底层也是使用查询构造器,所以方法类似:
$users = User::from(DB::raw('users FORCE INDEX (idx_email)'))
->where('email', '=', 'user@example.com')
->get();
使用 useIndex() 宏(自定义)
Laravel 本身没有内置 forceIndex() 方法,但你可以通过宏(Macro)注册一个:
// 在 AppServiceProvider 的 boot 方法中
use Illuminate\Database\Query\Builder;
Builder::macro('forceIndex', function ($index) {
$this->from(DB::raw($this->from . ' FORCE INDEX (' . $index . ')'));
return $this;
});
// 然后使用
$users = DB::table('users')
->forceIndex('idx_email')
->where('email', '=', 'user@example.com')
->get();
使用 USE INDEX 或 IGNORE INDEX
FORCE INDEX:强制优化器必须使用指定索引。USE INDEX:提示优化器优先考虑指定索引(但不强制,优化器可能忽略)。IGNORE INDEX:忽略指定索引。
// USE INDEX(提示)
DB::table(DB::raw('users USE INDEX (idx_email)'))
// IGNORE INDEX
DB::table(DB::raw('users IGNORE INDEX (idx_email)'))
在 JOIN 中强制索引
$users = DB::table('orders')
->join(DB::raw('users FORCE INDEX (idx_id)'), 'orders.user_id', '=', 'users.id')
->select('orders.*', 'users.email')
->get();
重要注意事项
- 仅适用于 MySQL:
FORCE INDEX是 MySQL 的语法,其他数据库(PostgreSQL、SQLite)不支持。 - 并不是“强制”保证:在某些极端情况下(如索引损坏、查询优化器认为强制索引导致成本更高),MySQL 仍可能忽略强制要求。
- 性能测试:强制索引应仅在你确定查询优化器选择了次优索引时使用,不恰当的使用可能导致性能下降。
- SQL 注入风险:如果索引名来自用户输入,务必进行转义或使用白名单验证(可以使用
$connection->getTablePrefix()处理表名前缀)。
检查是否生效
你可以通过 toSql() 或 dd() 查看生成的 SQL:
$sql = DB::table('users')
->from(DB::raw('users FORCE INDEX (idx_email)'))
->where('email', '=', 'test@example.com')
->toSql();
// 输出类似:select * from users FORCE INDEX (idx_email) where email = ?
替代方案:使用数据库索引提示
如果你使用的是 Laravel 9+,还可以考虑使用 indexHint() 方法(需要自定义或使用第三方包),不过最通用、最可控的还是 DB::raw() 方式。