【问题标题】:Extremely slow distinct query on indexed column索引列上的非常慢的不同查询
【发布时间】:2019-09-03 13:39:03
【问题描述】:

在 Postgres 数据库中,我在一个包含 3 亿行的大表中查询 MY_DATE 的不同值。大约有 400 个,MY_DATE 列已编入索引。

Select distinct  MY_DATE from MY_TABLE;

查询运行 22 分钟

在我的 Oracle DB 上使用完全相同的数据集和相同的索引定义的相同查询运行 11 秒。

查询计划显示查询正在使用索引:

EXPLAIN Select distinct  MY_DATE from MY_TABLE LIMIT 200;

给予:

QUERY PLAN
Limit  (cost=0.57..7171644.14 rows=200 width=8)
  ->  Unique  (cost=0.57..15419034.24 rows=430 width=8)
        ->  Index Only Scan using idx_obsdate on my_table  (cost=0.57..14672064.14 rows=298788038 width=8)

当我限制结果时,查询会变得更快。脑电图

Select distinct  MY_DATE from MY_TABLE LIMIT 5;

在亚秒内运行。

但是:

Select distinct  MY_DATE from MY_TABLE LIMIT 50;

已经需要几分钟了。使用LIMIT 子句,时间似乎呈指数增长。

我希望 Postgres 查询能在几秒钟内运行,就像我的 OracleDB 一样。 20 分钟的索引扫描——即使是一个大表——似乎也太离谱了。

有什么建议是什么导致了这个问题以及我能做什么?

【问题讨论】:

  • @GordonLinoff:Postgres 具有仅索引扫描。对于SELECT 查询,它不需要行锁。

标签: sql postgresql indexing query-optimization distinct


【解决方案1】:

不同的值...... 3 亿行......其中大约 400 个......列......被索引。

更多 更快的技术可以做到这一点。模拟loose index scan(又名跳过扫描),并假设my_date 定义为NOT NULL(或者我们可以忽略NULL 值):

WITH RECURSIVE cte AS (
   SELECT min(my_date) AS my_date
   FROM   my_table

   UNION ALL
   SELECT (SELECT my_date
           FROM   my_table 
           WHERE  my_date > cte.my_date
           ORDER  BY my_date
           LIMIT  1)
   FROM   cte
   WHERE  my_date IS NOT NULL
   )
TABLE  cte;

相关:

使用您提到的索引,它应该在 毫秒 内完成。

Oracle DB ... 11 秒。

因为 Oracle 有本机索引跳过扫描,而 Postgres 没有。在 Postgres 12 中有 ongoing efforts 来实现类似的功能。

目前(Postgres 11),虽然索引使用效果很好,但即使在仅索引扫描中,Postgres 也不能向前跳过,必须按顺序读取索引元组。如果没有LIMIT,则必须扫描完整的索引。因此,我们在您的 EXPLAIN 输出中看到:

仅索引扫描 ... rows=298788038

建议的新查询通过读取 400 个索引元组(每个不同值一个)实现相同的效果。 区别。

使用LIMIT(而不是ORDER BY!)就像您测试的那样,一旦检索到足够的行,Postgres 就会停止。增加限制具有线性效果。但是,如果每个不同值的行数可以变化,那么增加的成本也会变化。

【讨论】:

  • 感谢 Erwin 的精彩解释。不幸的是,您的查询似乎也运行缓慢: CTE Scan on cte (cost=1061000068.83..1061000070.85 rows=101 width=8) CTE cte -> Recursive Union (cost=7872574.05..1061000068.83 rows=101 width=8) -> WorkTable扫描 cte cte_1 (cost=0.00..105312749.28 rows=10 width=8) -> Finalize Aggregate (cost=7872574.05..7872574.06 rows=1 width=8) Filter: (observation_date IS NOT NULL) -> Gather (cost= 7872573.83..7872574.04 行=2 宽度=8)
  • 工人计划:2 -> 部分聚合(成本=7871573.83..7871573.84 行=1 宽度=8)-> 并行 Seq 扫描观察_2018feed_prod(成本=0.00..7562306.27 行=123707027 宽度=8 )
  • 这是否表明它将对 1.23 亿行进行并行扫描?
  • 我删除了索引并重新创建了索引。现在您的查询确实在 45 毫秒内运行!非常感谢。
  • @jkeys:可以高效实现。我建议你为你的问题开始一个新的question
猜你喜欢
  • 2015-02-06
  • 2013-12-04
  • 1970-01-01
  • 2014-10-16
  • 2020-05-10
  • 1970-01-01
  • 1970-01-01
  • 2016-06-29
相关资源
最近更新 更多