標題參考來源
大家好~
今天來簡單認識一下 N+1 query 吧!
假設我們查詢10篇文章與文章作者,
那麼這個查詢將會做11次查詢。
1次是查詢10篇文章,
另外10次是查詢文章的作者。
上述情況就是發生了 N+1 query。
另外在這篇文章內有用烤蛋糕的方式解釋 N+1 query,
挺有趣的~
來試試如何引發 N+1 query 吧~
目前我的資料庫內有10筆留言,
然後使用下方範例做關聯查詢。
use Illuminate\Support\Facades\DB;
public function index()
{
DB::listen(function ($query) {
var_dump($query->sql);
});
$messages = Message::all();
$messages->each->user;
return $messages;
}
為了方便測試,
我在該 function 內使用 DB::listen()
監聽事件。
下圖為測試結果:
如我們預期的發生 N+1 query 了~
那麼接下來讓我們認識該如何避免發生 N+1 query 吧!
在我的認知內,
加載關聯時用以下方式可以避免:
那麼來用 with()
解決剛剛發生的 N+1 query 吧~
public function index()
{
DB::listen(function ($query) {
var_dump($query->sql);
});
$messages = Message::with('user')->get();
return $messages;
}
也能用 load()
解決:
public function index()
{
DB::listen(function ($query) {
var_dump($query->sql);
});
$messages = Message::all();
$messages->load('user');
return $messages;
}
而 whenLoad()
就必須搭配 API Resources 一起使用,whenLoad()
會判斷有無加載關聯,
有加載才會顯示關聯資料,
進而避免 N+1 query。
至於用法的話,
我在 Day07 時有用到 whenLoad()
,
有興趣的夥伴可以參考一下~
在 Laravel 8 後,
有支援一種方式可以有效避免 N+1 query!
只要將 AppServiceProvider.php
的 boot()
中加入這一段:
Model::preventLazyLoading(! app()->isProduction());
完整使用方式如下:
use Illuminate\Support\ServiceProvider;
public function boot()
{
Model::preventLazyLoading(! app()->isProduction());
}
這樣設置後,
Laravel 會直接 Disabling Lazy Loading,
之後只要遇到 N+1 query,
Laravel 就會直接回報錯誤訊息給你喔!
當資料量大時,
N+1 query 產生的大量且多餘的查詢,
會對我們的資料庫造成效能上的負擔。
只要我們在使用 ORM 時稍加留意,
就可以有效避免發生 N+1 query 喔~
大家明天見啦~
若文章有任何問題,
還請大家不吝賜教!