name: ecto:n1-check description: 检测Ecto代码中的N+1查询模式。用于审查数据库访问模式或优化性能。
N+1查询检测
识别和修复Ecto/Phoenix应用程序中的N+1查询反模式。
铁律 - 切勿违反这些
- 永远不要在没有预加载的情况下访问关联 - 在
Enum.map之前始终预加载 - 循环内部不要调用Repo - 重构以批量查询
- 在上下文边界预加载 - 在上下文而非控制器/视图中加载关联
- 使用连接进行过滤 - 当通过关联过滤时,使用
join+preload
检测模式
模式1:Enum.map与Repo
# 错误:N+1查询
users
|> Enum.map(fn user -> Repo.get(Order, user.order_id) end)
# 正确:使用预加载的单次查询
users
|> Repo.preload(:orders)
模式2:无预加载的关联访问
# 错误:懒加载触发N次查询
for user <- users do
user.posts # 为每个用户触发查询!
end
# 正确:先急加载
users = Repo.all(User) |> Repo.preload(:posts)
for user <- users do
user.posts # 已加载
end
模式3:嵌套关联访问
# 错误:嵌套关联的N+1查询
user.posts |> Enum.map(fn post -> post.comments end)
# 正确:嵌套预加载
Repo.preload(user, posts: :comments)
快速检测命令
# 查找附近有Repo调用的Enum.map
grep -B5 -A5 "Enum.map" lib/ -r --include="*.ex" | grep -A5 -B5 "Repo\."
# 查找关联访问模式
grep -r "\.posts\|\.comments\|\.orders" lib/ --include="*.ex"
# 查找循环中的Repo调用
grep -B3 "Repo.get\|Repo.one" lib/ -r --include="*.ex" | grep -B3 "for\|Enum"
分析命令
对于上下文模块,运行:
grep -n "Repo\." lib/my_app/[context].ex
然后验证每个查询是否有适当的预加载。
参考资料
有关详细模式,请参见:
references/preload-patterns.md- 高效预加载策略references/query-optimization.md- 查询批处理技术