【发布时间】:2014-12-02 10:26:08
【问题描述】:
我对数据库相当缺乏经验,并且刚刚阅读了"n+1 selects issue"。我的后续问题:假设数据库与我的程序位于同一台机器上,缓存在 RAM 中并正确索引,为什么 n+1 查询模式很慢?
作为示例,让我们从接受的答案中获取代码:
SELECT * FROM Cars;
/* for each car */
SELECT * FROM Wheel WHERE CarId = ?
根据我的数据库缓存心智模型,每个SELECT * FROM Wheel WHERE CarId = ? 查询都应该需要:
- 1 次查找以到达“Wheel”表(一个哈希图
get()) - 1 次查找以到达具有指定
CarId的 k 个轮子列表(另一个哈希图get()) - k 次查找以获取每个匹配车轮的车轮行(k 指针取消引用)
即使我们将其乘以一个小的常数因子以增加由于内部内存结构的开销,它仍然应该快得不明显。进程间通信是瓶颈吗?
编辑:我刚刚通过黑客新闻找到了这篇相关文章:Following a Select Statement Through Postgres Internals. - HN discussion thread。
编辑 2:为了澄清,我确实假设 N 很大。那么,一个不平凡的开销将增加一个明显的延迟,是的。我在问为什么首先,对于上述设置,开销并非微不足道。
【问题讨论】:
-
因为一条语句检索 n+1 行几乎总是比 n+1 条语句快。解析 SQL、准备执行计划和返回数据都会产生开销(取决于 DBMS,解析阶段可能是相当大的开销)。此外:你会读一份蛋糕的食谱,然后去超市买面粉,回家读食谱,回去买鸡蛋,回家读食谱,回去买黄油,......
-
“因为一条语句检索 n+1 行几乎总是比 n+1 条语句快。” - 我看到了,但令我惊讶的是,人们用 Ruby 或 Python 而不是 C 编写程序实际上需要更长的时间。我发现的解决方案有时看起来比原始代码复杂得多。重新蛋糕食谱:但如果配料就在另一个房间里,我可能不会太在意去两次或三次;)。
-
重新查询解析开销:鉴于我们已经在任何地方使用准备好的语句,这难道不是一个开发良好的库应该解决的问题吗?或者这是否会破坏公共接口,我们又回到了 IPC 开销。 (@a_horse_with_no_name)
-
“一个完善的库应该解决的问题” - 例如,Python caches compiled regular expressions by default。
-
进程间通信慢。
标签: sql orm select-n-plus-1