什麼是 N+1 問題,以及如何解決

N+1 是ORM(對象關係映射)世界中的一個問題。sql

在介紹什麼是N+1問題以前,首先思考一個問題:數據庫

假設如今有一個用戶表和一個餘額表,這兩個表經過user_id進行關聯。如今有一個需求是查詢年齡大於18歲的用戶,以及用戶各自的餘額框架

這個問題並不難,但對於新手而言,可能經常會犯的一個錯誤就是在循環中進行查詢。性能

$users = User::where("age", ">", 18)->select();
foreach($users as $user){
  $balance = User::getFieldByUserId($user->user_id, "balance");
  $user['balance'] = $balance;
}

這樣作是很是糟糕的,數據量小還少,在數據量較大的狀況下,是很是消耗數據庫性能的。this

經過Mysql 查詢日誌,能夠看到查詢用戶表是一次,由於有四個符合該條件的用戶,查詢用戶表關聯的餘額表是四次。spa

N+1問題就是這樣產生的:查詢主表是一次,查詢出N 條記錄,根據這N 條記錄,查詢關聯的副(從)表,共須要N 次。因此,應該叫1+N 問題更合適一些。日誌

其實,若是稍微瞭解一點SQL,根本不用這麼麻煩,直接使用JOIN 一次就搞定了。code

有時候是否是以爲ORM 也挺礙事的。對象

對於這類問題,ORM 其實爲咱們提供了相應的方案,那就是使用withblog

with

$users = User::where("age", ">", 18)
        ->with("hasBalance")
        ->select();

hasBalance 是什麼呢?

它是在User模型中定義的一個方法:

class User extends Model
{
    //  ...
    
    public function hasBalance()
    {
          return $this->hasOne(Balance::class, "user_id", "user_id");
    }
}

經過這個方法讓User 模型與Balance 模型進行一對一關聯。

如今再來看一下Mysql 的查詢日誌:

能夠很清楚的看到,總查詢次數由原來的1+N 變成了如今的1+1

總結

N+1 問題是什麼?會形成什麼影響?應該如何解決?

  1. 執行一次查詢獲取N 條主數據後,因爲關聯引發的執行N 次查詢從數據
  2. 帶來了沒必要要的查詢開銷
  3. 能夠經過框架 ORM 自帶的with 去解決
相關文章
相關標籤/搜索