我希望添加一些实用方法来帮助避免很多 n+1 issues在遗留应用程序中。
常见的模式是这样的:
select a.* /* over 10 columns */
from [table-A] a
where /* something */
检索到收藏
ClassA
记录实例然后子实例被延迟检索:
select b.* /* over 10 columns */
from [sub-table-B] b
where b.ParentId = @ClassA_ID
这会导致 n+1 选择问题。大多数情况下,这不是主要问题,因为只有几个
ClassA
实例是在一个不常被点击的页面上检索的,但是在越来越多的地方,这个 n+1 问题会导致页面随着应用程序的扩展而变得太慢。我正在寻找替换此应用程序现有数据访问代码的一部分,以便
ClassA
实例和 ClassB
实例一起检索。我认为有3种方法可以做到:
1) 获取
ClassA
像我们现在所做的那样,然后得到 ClassB
一个聚合调用中的实例:select b.*
from [sub-table-B] b
where b.ParentId in ( /* list of parent IDs */ )
这是两个独立的 DB 调用,动态 SQL 的查询计划将无法缓存(由于 ID 列表)。
2) 获取
ClassB
带有子查询的实例:select b.*
from [sub-table-B] b
inner join [table-A] a
on b.ParentId = a.[ID]
where /* something */
这也是两次DB调用,查询
[table-A]
必须评估两次。3) 聚在一起并删除
ClassA
的重复数据实例:select a.*, b.*
from [table-A] a
left outer join [sub-table-B] b
on a.[ID] = b.ParentId
where /* something */
这只是一次数据库调用,但现在我们得到了
[table-A]
的内容重复 - 结果集会更大,并且将数据从数据库发送到客户端的时间会更多。所以实际上这是 3 种可能的妥协:
我可以为任何一对父子表测试这三种模式,但我有很多。我想知道的是哪种模式始终更快?更重要的是为什么?这些妥协之一是明显的性能杀手吗?
Linq、EF 和 NHibernate 等现有机制使用什么?
有没有比所有 3 种方法都更好的第 4 种方法?
最佳答案
我认为 EF 和 L2S 使用您的第三种方法 - 绝对只有一个 db 调用。
通常,更多的 db 往返比具有更大结果集的更少的 db 往返花费更多的时间。
也许在某些边缘情况下,您在表 A 中有大量数据,而更大的结果集会增加向客户端的传输时间太多。
但这主要是数据库服务器和客户端之间的延迟和带宽问题。
第 4 种方法可能是编写一个返回多个结果集的存储过程。您只查询需要的记录的每个表对应一个。这适合您的第一种方法,但减少为一次往返。但这会使事情变得有点复杂,并且不像其他方法那样灵活。
关于sql - 哪个是避免 n+1 问题的最快执行方法,为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7526227/