N+1
是 ORM(对象关系映射)世界中的一个问题。
在介绍什么是 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;
}
这样做是十分蹩脚的,数据量小还少,在数据量较大的状况下,是十分耗费数据库性能的。
通过 Mysql 查问日志,能够看到查问用户表是一次,因为有四个合乎该条件的用户,查问用户表关联的余额表是四次。
N+1
问题就是这样产生的:查问主表是一次,查问出 N 条记录,依据这 N 条记录,查问关联的副(从)表,共须要 N 次。所以,应该叫1+N
问题更适合一些。
其实,如果略微理解一点 SQL,基本不必这么麻烦,间接应用JOIN
一次就搞定了。
有时候是不是感觉 ORM 也挺碍事的。
对于这类问题,ORM 其实为咱们提供了相应的计划,那就是应用with
。
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
问题是什么?会造成什么影响?应该如何解决?
- 执行一次查问获取 N 条主数据后,因为关联引起的执行 N 次查问从数据
- 带来了不必要的查问开销
- 能够通过框架 ORM 自带的
with
去解决