【问题标题】:How does PostgreSQL perform ORDER BY with a b-tree index on the field?PostgreSQL 如何在字段上使用 b-tree 索引执行 ORDER BY?
【发布时间】:2015-10-13 03:03:40
【问题描述】:

我有一张桌子bsort

CREATE TABLE bsort(a int, data text);

此处data 可能不完整。换句话说,有些元组可能没有data 值。

然后我在表上建立一个 b-tree 索引:

CREATE INDEX ON bsort USING BTREE(a);

现在如果我执行这个查询:

SELECT * FROM bsort ORDER BY a;

PostgreSQL 是对具有 nlogn 复杂度的元组进行排序,还是直接从 b-tree 索引中获取顺序?

【问题讨论】:

  • 查看执行计划你会看到

标签: postgresql sorting indexing sql-order-by postgresql-performance


【解决方案1】:

对于像这样的简单查询,Postgres 将使用index scan 并按顺序从索引中检索易于排序的元组。由于其MVCC model,Postgres 必须始终访问“堆”(数据页),以验证条目对当前事务实际上是可见的。引用Postgres Wiki on index-only scans

PostgreSQL 索引不包含可见性信息。这就对了 无法直接确定任何给定的元组是否对 当前的交易,这就是为什么仅索引需要这么长时间 要实施的扫描。

这最终发生在版本 9.2 中:index-only scansThe manual:

如果索引存储原始索引数据值(而不是一些 它们的有损表示),支持index-only scans很有用,其中索引返回实际数据而不仅仅是TID 堆元组。如果可见性映射显示,这只会避免 I/O TID 在一个全可见的页面上;否则堆元组必须是 无论如何访问以检查 MVCC 可见性。但这并不关心 访问方法的。

visibility map 决定是否可以进行仅索引扫描。如果所有涉及的列值都包含在索引中,则只有一个选项。否则,在任何情况下都必须(另外)访问堆。 仍然不需要排序步骤。

这就是为什么我们现在有时会将其他无用的列添加到索引中。就像您示例中的 data 列一样:

CREATE INDEX ON bsort (a, data);  -- btree is the default index type

它使索引更大(取决于)并且维护和用于其他目的的成本更高。因此,如果您从中获得仅索引扫描,请仅附加 data 列。索引中列的顺序很重要:

从 Postgres 11 开始,还有带有 INCLUDE 关键字的“覆盖索引”。喜欢:

CREATE INDEX ON bsort (a) INCLUDE (data);

见:

仅索引扫描的好处,per documentation:

如果已知页面上的所有元组都是可见的,则堆取 可以跳过。这在大型数据集上最为明显,其中 可见性映射可以防止磁盘访问。能见度地图很大 比堆小,所以即使是堆也可以很容易地被缓存 很大。

可见性映射由VACUUM 维护,如果您运行autovacuum(现代 Postgres 中的默认设置),则会自动发生这种情况。详情:

但是在对表的写入操作和下一次VACUUM 运行之间存在一些延迟。要点:

  • 只读表在清理后随时准备进行仅索引扫描。
  • 已修改的数据页面在可见性映射中丢失其“全部可见”标志,直到下一个VACUUM(以及所有较旧的事务都完成),因此它取决于写入操作和VACUUM 频率之间的比率.

如果涉及的页面中的部分被标记为全部可见,则仍然可以进行部分索引扫描。但是如果无论如何都必须访问堆,访问方法“索引扫描”会便宜一些。因此,如果当前有太多页面是脏的,Postgres 将完全切换到更便宜的索引扫描。 The Postgres Wiki again:

作为预计的堆提取(或“访问”)次数 规划者需要的上升,规划者最终会得出结论 仅索引扫描是不可取的,因为它不是最便宜的 根据其成本模型制定可能的计划。 index-only 的值 扫描完全在于它们允许我们省略堆访问的潜力 (如果只是部分)并最小化 I/O。

【讨论】:

  • 值得注意的是尝试按不同排序规则排序的复杂性,也许是因为字符数据的排序相当复杂。 postgresql.org/docs/9.4/static/indexes-collations.html
  • @DavidAldridge:当然,这是相关的,但前提是ORDER BY 涉及字符类型列,本例中并非如此。添加的data 列的类型为text,但ORDER BY 仅列出integera,因此无需考虑排序规则。
  • 确实如此,但我不认为你会喜欢回答另一个完全相同的问题,除了它使用 TEXT 列;)
  • @DavidAldridge:因此,您的评论是有价值的补充。
  • @ErwinBrandstetter 。 . .我很想说你的答案总是很有趣。
【解决方案2】:

您需要检查执行计划。但是,Postgres 非常有能力使用索引来提高order by 的效率。它将直接从索引中读取记录。因为您只有一列,所以无需访问数据页。

【讨论】:

  • 如果表格多于一列怎么办?它还会从索引中读取记录吗?
  • 那将是一个不同的问题,但它应该。然后它需要返回数据页以获取其余列。
猜你喜欢
  • 1970-01-01
  • 2010-12-12
  • 1970-01-01
  • 1970-01-01
  • 2018-01-17
  • 2020-12-14
  • 2013-05-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多