数据库查询的N+1问题
在工作中有时也会涉及到后端开发,后端开发的性能优化多是查询优化。类似前端 DOM 操作很消耗性能,后端开发数据库查询次数也会相当消耗性能。我们在开发中应该尽量减少 SQL 查询次数,避免 N+1 问题来提升性能。接下来就来介绍数据库查询的 N+1 问题。
# SQL N+1
举一个简单的例子,数据库中有一张 cars
表存储汽车的相关信息,还有一张 wheels
表存储汽车的轮胎信息。
查询汽车的语句是:
SELECT * FROM cars;
1
查询每个汽车的轮子的语句是:
SELECT * FROM wheels WHERE car_id = ${car_id};
1
这里的 N 就是汽车的数量,如果需要知道每个汽车的轮胎信息那就正好需要查询 N+1
次。
# 解决 N+1
如果汽车和轮胎是一对一的关系可以通过使用 join
语句来减少数据库查询次数。把查询降为 1 次。修正之后的查询语句是:
SELECT cars.*, wheels.* FROM cars INNER JOIN wheels ON wheels.car_id = cars.id
1
如果汽车和轮胎是一对多的关系可以通过使用:
SELECT * FROM cars;
SELECT * FROM wheels WHERE wheels.car_id IN (1,2,3)
1
2
2
一次性查询所有需要的轮胎把查询降为两次。
# 现有框架解决方案
# Django
Django 可以使用select-related (opens new window) 和 prefetch-related (opens new window)来解决这个问题。
select-related
主要用于一对一的关系,而 prefetch-related
主要用于多对多或者多对一的关系。
# Ruby on Rails
使用 includes (opens new window) 来解决 N+1 问题。
# 总结
后端开发需要时刻注意 N+1 问题, 在开发模式下,日志会输出 SQL 查询语句,需要多加注意。也可以使用一些工具来检测这个问题。