SQL: N+1
适用于 ✅ 开源版 ✅ 专业版 ✅ 企业版
"太好了,我们正在使用 jOOQ (即 SQL),所以我们摆脱了 N+1 问题。"
嗯,有一类 N+1 问题 jOOQ 永远不会遇到。 这些是由于延迟加载而发生的“意外” N+1 查询。 使用 jOOQ,一切总是“急切地”加载,正如您在查询中指定的那样。 与 ORM 不同,急切加载也不是自动的。 jOOQ 不会自行实现对象图的大部分内容。 一切都是明确完成的。 但这意味着您仍然会遇到显式 N+1 问题
我们的诊断模块可以自动检测重复的查询,这些查询通常是由 N+1 问题引起的。 请参阅重复语句
一个由 jOOQ 查询引起的 N+1 问题的例子
// 1 query for (Integer id : create .select(AUTHOR.ID) .from(AUTHOR) .fetch(AUTHOR.ID) ) { // N queries List<Integer> books = create.select(BOOK.ID) .from(BOOK) .where(BOOK.AUTHOR_ID.eq(AUTHOR.ID)) .fetch(BOOK.ID); }
N+1 问题(技术上应该命名为 1+N 问题)可以在上面很容易地看到
-
执行
1
个查询来获取所有AUTHOR
记录。 -
执行
N
个查询来为每个AUTHOR
获取所有BOOK
记录。
这个特定的查询最好使用一个简单的 SQL JOIN来实现
// 1 query for (Record2<Integer, Integer> record : create .select(AUTHOR.ID) .from(AUTHOR) .join(BOOK) .on(BOOK.AUTHOR_ID.eq(AUTHOR.ID)) ) { // No additional queries needed... }
您可以使用 jOOQ 的许多映射功能之一将您的集合直接嵌套到您的 Java 逻辑中,或者使用MULTISET 或 MULTISET_AGG 将集合直接嵌套到 SQL 中,只要您不多次往返数据库即可。
N+1 不是严格与 SQL 相关的问题。 它只是因为 ORM 强调通过延迟加载自动填充对象图而不是强调查询而流行起来。 然而,根本问题是客户端(您的 Java 代码)和服务器(您的 RDBMS)之间的延迟,这是由过多的往返行程引起的。 请参阅此博客文章,了解一个比较调用存储过程1
次来获取N
个项目与调用另一个存储过程N
次来获取1
个项目的示例,每次:https://blog.jooq.org/the-cost-of-jdbc-server-roundtrips/。 结果是一样的。
但是对于 SQL,情况会变得更糟。 如果您将整个声明性查询推送到数据库中,数据库可以自由选择各种符合条件的算法来生成结果(例如,哈希连接与嵌套循环连接等)。 如果您自己循环遍历N
个父行,则您正在强制执行嵌套循环连接,这可能会更糟,除了额外的延迟之外,以防哈希连接或合并连接会更好。
反馈
您对此页面有任何反馈吗? 我们很乐意听到它!