加速索引扫描向后查询

kce*_*los 7 postgresql index index-tuning postgresql-performance

我的应用程序正在执行以下 psql 查询,并且运行速度非常慢:

SELECT COUNT(*) 
FROM (
  SELECT 1 AS one 
  FROM "large_table" 
  WHERE "large_table"."user_id" = 123 
  ORDER BY "large_table"."id" desc 
  LIMIT 1 OFFSET 30
) subquery_for_count;
Run Code Online (Sandbox Code Playgroud)

当我将 更改ORDER BY为时ASC,它的运行速度快了 100 倍。我在 id 上有默认的主键索引,并且我已经尝试按降序顺序为 id 添加附加索引,但这似乎没有什么区别。

当我运行解释分析时,我发现它在慢速查询 ( ) 上使用向后索引扫描desc。我尝试手动禁用会话的索引扫描,发现查询运行时间为 40 秒,而不是 2 分钟,这是一个显着的改进。

知道如何在按 DESC 排序时尝试提高此查询的速度吗?我读过,对于 b 树索引,无论排序顺序如何,它通常应该为您提供相同的性能,但情况似乎并非如此。

jja*_*nes 7

您的查询必须使用“id”上的索引以隐式顺序扫描索引,然后过滤掉“user_id”不等于 123 的所有内容,在找到 31 行通过过滤器后停止。朝一个方向走很快就能找到 31 个这样的行,朝另一个方向走需要在 31 个幸存之前过滤掉大量行(因为从该端开始的行中没有/很少有 user_id=123)。

您可以通过对查询进行 EXPLAIN (ANALYZE, BUFFERS) 轻松确认这一理论。

这从根本上来说与索引扫描的顺序无关。如果您为 123 选择一个具有相反属性的值(它们都出现在索引的逻辑末尾而不是逻辑开头),那么情况将相反。指定 DESC 将解决问题,而不是导致问题。

知道如何在按 DESC 排序时尝试提高此查询的速度吗?

你的询问似乎毫无意义。计数不是依赖于顺序的活动。这可能不是您真正的查询。那么谁知道我们的建议是否会转移到您的实际查询中呢?此查询最直接的修复方法是在 (user_id, id) 上构建多列索引。这样就不会一一过滤掉任何行,因为它们将通过索引操作批量删除。


小智 2

文档中有关于此的讨论(https://www.postgresql.org/docs/current/indexes-ordering.html):

默认情况下,B 树索引按升序存储其条目,最后是空值。这意味着对列 x 上的索引进行正向扫描会生成满足 ORDER BY x(或更详细地说,ORDER BY x ASC NULLS LAST)的输出。索引也可以向后扫描,产生满足 ORDER BY x DESC 的输出(或更详细地说,ORDER BY x DESC NULLS FIRST,因为 NULLS FIRST 是 ORDER BY DESC 的默认值)。

您可以在创建索引时通过包含选项 ASC、DESC、NULLS FIRST 和/或 NULLS LAST 来调整 B 树索引的顺序

...剪断...

您可能想知道为什么要提供所有四个选项,当两个选项加上向后扫描的可能性将涵盖 ORDER BY 的所有变体时。在单列索引中,这些选项确实是多余的,但在多列索引中它们可能很有用。

如果查询规划器选择了多列索引,您可能会遇到文档描述的情况。如果它使用单列索引,那么您观察到的性能是不寻常的。

我建议您创建一个降序索引,看看是否可以提高性能:

CREATE INDEX test3_desc_index ON test3 (id DESC NULLS LAST);
Run Code Online (Sandbox Code Playgroud)