编程

在 Laravel 中使用 Auto Eager Loading 全局防止 N+1 问题

52 2025-05-17 11:15:00

Laravel 中的 Eager Loading 是一种在查询模型时加载相关模型的方法。这样做是为了防止 N+1 问题,当加载一个模型,并在随后逐一加载其关联模型时,会导致对数据库的多个查询。

这可能是一个性能问题,尤其是当你有很多相关的模型时。快速加载(Eager Loading)允许你在单个查询中加载所有相关模型,这可以显著提高性能。

例如,如果你有一个包含许多帖子(Post)模型的用户(User)模型,你可以像这样快速加载帖子:

$users = User::with('posts')->get();

foreach ($users as $user) {
    // This will not run a separate query for each user
    echo $user->posts->title; 
}

这将会在单个查询中加载所有帖子,而不会像如下这样一个个逐一加载:

$users = User::all();

foreach ($users as $user) {
    // This will run a separate query for each user
    echo $user->posts->title; 
}

这将会导致 N+1 问题,其中有一个查询来加载所有的用户,然后每个用户一个查询来加载他们的帖子。

如你所见,Laravel 中 Eager Loading 并不是默认行为。你必须使用 with() 方法显式告知 Laravel 快速加载关联模型。

要修复这个问题,Laravel 现在有一个方式全局启用 Eager Loading 使得可以快速自动加载模型。

全局自动 Eager Loading

Laravel 12.x 在 Model 中引入一个新的方法叫做 automaticallyEagerLoadRelationships()。该方法允许你启用在应用中跨越所有模型启用自动 Eager Loading。

重要:此功能仍处于测试阶段,Laravel 团队正在收集社区对此的反馈。因此,将来可能会此功能进行一些更改。

将下列代码添加到 AppServiceProvider 可以启用该功能:

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Model::automaticallyEagerLoadRelationships();
    }
}

因此,现在,正如我之前所展示的,即使是有 N+1 问题的有问题的代码,也可以正常工作。

// Since auto eager loading is enabled globally, 
// this will work without any N+1 issues
$users = User::all();

foreach ($users as $user) {
    echo $user->posts->title; 
}

模型自动 Eager Loading

如果你不想全局启用自动热加载,你也可以在每个模型基础上启用它。你可以通过在模型中添加 automaticallyEagerLoadRelationships() 方法来实现这一点。

use App\Models\User;

User::automaticallyEagerLoadRelationships();

在查询上自动 Eager Loading

你还可以在每个查询的基础上启用自动渴望加载。可以通过使用 withRelationshipAutoloading() 方法来实现这一点,如下所示:

$users = User::all()->withRelationshipAutoloading();

foreach ($users as $user) {
    echo $user->posts->title; 
}

结论

我认为这是 Laravel 中一个很好的功能,对于不熟悉渴望加载的新手来说,这可能是一个救星。