下面带大家介绍关于优化Laravel数据库查询的18个技巧,希望对大家有所帮助!
如果应用运行缓慢或存在大量数据库查询,请按照以下性能优化提示来缩短应用的加载时间。
1. 检索大型数据集
本提示主要侧重于提高处理大型数据集时应用的内存使用率。
处理大的集合时,分组检索结果处理,而不是一次性检索处理。
如下展示了从 posts 表检索数据的过程。
$posts = Post::all(); // 使用 eloquent$posts = DB::table('posts')->get(); // 使用查询构造器foreach ($posts as $post){ // 处理 posts 操作}上面的例子会从 posts 表检索所有的记录并处理。如果这个表达到了 100 多万行呢?内存将很快被耗尽。
为了避免在处理大型数据集时出现问题,我们可以检索结果子集并按照下面的方式处理它们。
选项 1: 使用 chunk
// 当使用 eloquent 时$posts = Post::chunk(100, function($posts){ foreach ($posts as $post){ // Process posts }});// 当使用查询构造器时$posts = DB::table('posts')->chunk(100, function ($posts){ foreach ($posts as $post){ // Process posts }});以上例子从 posts 表中检索 100 条记录对其进行处理,另外再检索 100 条记录进行处理。此迭代将继续,直到处理完所有记录。
这种方法将创建更多的数据库查询,但内存效率会更高。 通常, 大型数据集的处理应该再后台进行。因此,可以在后台运行时进行更多查询,以避免在处理大型数据集时耗尽内存。
选项 2: 使用游标
// 使用 eloquentforeach (Post::cursor() as $post){ // 处理单个 post}// 使用 query 构建器foreach (DB::table('posts')->cursor() as $post){ // 处理单个 post}示例进行单个数据库查询,检索表的所有记录,一个接一个一个处理 Eloquent 模型。这种方式仅查询一次数据库,得到全部 posts 。 但使用 php 生成器 优化内存使用。
什么情况使用这个呢?
这能够在应用层极大地优化内存使用,由于我们检索表的所有数据,数据库内存占用任然很高。
在数据库内存较多,应用内存较少的时候,建议使用游标。然而,如果你的数据库没有足够的内存,最好使用 chunks 。
选项 3: 使用 chunkById
// 使用 eloquent$posts = Post::chunkById(100, function($posts){ foreach ($posts as $post){ // 处理 posts }});// 使用 query 构造器$posts = DB::table('posts')->chunkById(100, function ($posts){ foreach ($posts as $post){ // 处理 posts }});chunk 和 chunkById 最大的区别是 chunk 通过offset 和 limit 检索数据。然而chunkById 通过id 字段检索结构。id 字段通常是整型字段,而且它也是自增字段。
chunk 和 chunkById 的查询如下。
chunk
select * from posts offset 0 limit 100
select * from posts offset 101 limit 100
chunkById
select * from posts order by id asc limit 100
select * from posts where id > 100 order by id asc limit 100
通常,查询使用 limit 和 offset 是较慢的,尽量避免使用。本文 详细介绍使用 offset 的问题。
chunkById 使用 id 整型字段,通过 where clause 查询,这样会更快。
什么时候使用 chunkById ?
当数据库存在自增 主键 的时候使用。
2. 选择合适的列
通常从数据库检索数据时,会像下面这样做。
$posts = Post::find(1); // 使用 eloquent$posts = DB::table('posts')->where('id','=',1)->first(); // 使用 query 构建器上面的代码会得到如下的查询
select * from posts where id = 1 limit 1
select * 表示从表中查出所有列。
当需要所有列时,这没有问题。
然而,仅需要指定的列(id,title)时,只需要像下面这样检索那些列。
$posts = Post::select(['id','title'])->find(1); // 使用 eloquent$posts = DB::table('posts')->where('id','=',1)->select(['id','title'])->first(); // 使用 query 构建器上面代码得到如下查询
select id,title from posts where id = 1 limit 1
3. 当需要数据库表的一两个列时
这点主要关注对检索结果的处理时间。这不影响实际的查询时间。
如我上面提到的,检索指定的列,可以这样做
$posts = Post::select(['title','slug'])->get(); // 使用 eloquent$posts = DB::table('posts')->select(['title','slug'])->get(); // 使用 query 构建器执行上面的代码,它会在幕后执行以下操作。
执行
select title, slug from posts查询检索出的每一行对应一个
Post模型对象(对 PHP 对象)(query 构建器得到标准的 PHP 对象)为
Post模型生成 collection返回 collection
访问数据
foreach ($posts as $post){ // $post 是 Post 模型或 php 标准对象 $post->title; $post->slug;}上面的方式有额外的开销,为每一行创建 Post 模型,并为这些对象创建一个集合。如果的确需要 Post 模型实例而不是数据,这是最正确的做法。
但如果您只需要两个值时,则可以执行以下操作:
$posts = Post::pluck('title', 'slug'); // 使用 eloquent 时$posts = DB::table('posts')->pluck('title','slug'); // 使用查询构造器时当上面代码被执行时,它在幕后会执行以下操作。
对数据库执行
select title, slug from posts查询创建一个数组,其中会以
title作为数组值,slug作为数组键返回数组 ( 数组格式:
[ slug => title, slug => title ])
要访问结果,我们可以这么做
foreach ($posts as $slug => $title){ // $title 是 post 的 title /

