lemontree863.github.io

什么是n+1查询?

比如说,一辆汽车有4个轮子。现在要查询品牌为”丰田”的汽车,伪代码如下:

public class Car{
    int id;
    string brand;// 品牌
    List<Wheel> wheelList;// 轮子
}

List<Car> list = listCar('丰田');
for(Car car : list){
     List<Wheel> wheelList = listWheel(car);
     car.setWheelList(wheelList); 
}

相对应的sql操作就是

-- N+1中的1
select * from tb_car where brand = '丰田'

然后遍历返回的结果,假设数量为N

-- N+1中的N,循环查询
select * from tb_car_wheel where car_id = ?

select * from tb_car_wheel where car_id = ?

select * from tb_car_wheel where car_id = ?
...

当N值很大时,性能会很低。即使查询tb_car_wheel的单条sql只要5ms,但是当N=1000时,耗时就长达5s。

如何避免N+1?

1、批量查询,然后在内存中组装汽车轮子

select * from tb_car_wheel where car_id in (?,?,?,?,?...)

2、关联表

select t.*,t2.* from tb_car t 
left join tb_car_wheel t2
on t.id = t2.car_id
where brand = '丰田';

orm框架会自动将查询结果组装汽车轮子。

注意

通过配置单独查询sql的方式使用mybatis的association元素,会引起N+1问题。

<!-- 不要这么用 -->
<resultMap id = "" type = "">
    <association property="wheel" ,select="selectWheel"
</>