一、N+1問題
介紹
在基本級別,ORM 是 “懶惰” 加載相關(guān)的模型數(shù)據(jù)。但是,ORM 應(yīng)該如何知道你的意圖?在查詢模型后,您可能永遠(yuǎn)不會真正使用相關(guān)模型的數(shù)據(jù)。不優(yōu)化查詢被稱為 “N + 1” 問題。當(dāng)您使用對象來表示查詢時,您可能在不知情的情況下進(jìn)行查詢。
想象一下,您收到了100個來自數(shù)據(jù)庫的對象,并且每條記錄都有1個關(guān)聯(lián)的模型(即belongsTo)。使用ORM默認(rèn)會產(chǎn)生101條查詢; 對原始100條記錄 進(jìn)行一次查詢,如果訪問了模型對象上的相關(guān)數(shù)據(jù),則對每條記錄進(jìn)行附加查詢。在偽代碼中,假設(shè)您要列出所有已發(fā)布帖子的發(fā)布作者。從一組帖子(每個帖子有一位作者),您可以得到一個作者姓名列表,如下所示:
$posts = Post::published()->get(); // 一次查詢
$authors = array_map(function($post) {
??? // 生成對作者模型的查詢
??? return $post->author->name;
}, $posts);
我們并沒有告訴模型我們需要所有作者,因此每次從各個Post?模型實例中獲取作者姓名時都會發(fā)生單獨(dú)的查詢 。
解決
預(yù)加載功能
使用with()方法指定想要預(yù)加載的關(guān)聯(lián):
$users = User::where(“age”, “>”, 18)
??????? ->with(“hasBalance”)
??????? ->select();
hasBalance?是什么呢?
它是在User模型中定義的一個方法:
class User extends Model
{
??? //? …
??? // User模型與Balance 模型進(jìn)行一對一關(guān)聯(lián)
??? public function hasBalance()
??? {
????????? return $this->hasOne(Balance::class, “user_id”, “user_id”);
??? }
}
通過這個方法讓User?模型與Balance?模型進(jìn)行一對一關(guān)聯(lián)。
延伸閱讀:
二、N+1 問題由來
假設(shè)需要現(xiàn)在需要查找模型 A 的 n 條數(shù)據(jù),而模型 A 又關(guān)聯(lián)了另外多個的模型,這里假設(shè) A 關(guān)聯(lián)了模型 B 和 C. 現(xiàn)在需要查找 n 條 A 的記錄(這里需要 1 條 SQL 語句,例如 select * from A ),同時把這 n 條記錄關(guān)聯(lián)的 B, C 記錄也查出來(這里需要 n 條查詢 B 的語句和 n 條查詢 C 的語句),于是最后需要的 SQL 語句數(shù)目為 2n + 1。
將 2n 抽象出來,假設(shè)需要查找的是 n 條 A 的記錄(count(SQL) = 1),同時需要把與 A 關(guān)聯(lián)的 k 個模型的記錄也查找處理出來( count(SQL) = n * k ), 設(shè) n * k = N, 最后查詢的 SQL 語句數(shù)目,即 N + 1。