Laravel5.5+中通过即时加载关系/预加载嵌套关系避免n+1查询问题测试demo
在数据库查询优化中,这条建议你可能听说过无数次了。
测试demo中,所以我会尽可能简短。
让我们假设您有以下场景:
控制器:
class PostController extends Controller
{
public function index()
{
$posts = Post::all();
return view('posts.index', ['posts' => $posts ]);
}
}
视图:
// posts/index.blade.php 文件
@foreach($posts as $post)
<li>
<h3>{{ $post->title }}</h3>
<p>Author: {{ $post->author->name }}</p>
</li>
@endforeach
上面的代码是检索所有的帖子,并在网页上显示帖子标题和作者,假设帖子模型关联作者。
执行以上代码将导致运行以下查询。
select * from posts // 假设返回5条数据
select * from authors where id = { post1.author_id }
select * from authors where id = { post2.author_id }
select * from authors where id = { post3.author_id }
select * from authors where id = { post4.author_id }
select * from authors where id = { post5.author_id }
如上,
1 条查询来检索帖子,5 条查询来检索帖子的作者(假设有 5 篇帖子)。
因此对于每篇帖子,都会进行一个单独的查询来检索它的作者。
所以如果有 N 篇帖子,将会产生 N+1 条查询(1 条查询检索帖子,N 条查询检索每篇帖子的作者)。
这常被称作 N+1 查询问题。
避免这个问题,可以像下面这样预加载帖子的作者。
预加载关系:
$posts = Post::all(); // Avoid doing this
$posts = Post::with(['author'])->get(); // Do this instead
预加载嵌套关系(基于上面的例子基础上)
从上面的例子,考虑作者归属于一个组,同时需要显示组的名字的情况。因此在 blade 文件中,可以按下面这样做。
@foreach($posts as $post)
<li>
<h3>{{ $post->title }}</h3>
<p>Author: {{ $post->author->name }}</p>
<p>Author's Team: {{ $post->author->team->name }}</p>
</li>
@endforeach
接着
$posts = Post::with(['author'])->get();
得到下面的查询:
select * from posts // Assume this query returned 5 posts
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )
select * from teams where id = { author1.team_id }
select * from teams where id = { author2.team_id }
select * from teams where id = { author3.team_id }
select * from teams where id = { author4.team_id }
select * from teams where id = { author5.team_id }
如上,尽管预加载了 authors 关系,仍然产生了大量的查询。
这是因为没有预加载 authors 上的 team 关系。
通过下面这样来解决这个它。
预加载嵌套关系:
$posts = Post::with(['author.team'])->get();
执行得到下面的查询。
select * from posts // Assume this query returned 5 posts
select * from authors where id in( { post1.author_id }, { post2.author_id }, { post3.author_id }, { post4.author_id }, { post5.author_id } )
select * from teams where id in( { author1.team_id }, { author2.team_id }, { author3.team_id }, { author4.team_id }, { author5.team_id } )
通过预加载嵌套关系,可以将查询数从 11 减到 3。
以上就是通过即时加载关系/预加载嵌套关系避免n+1查询问题
相关文章