索引列的极慢非重复查询

时间:2019-04-12 18:23:04

标签: sql postgresql indexing query-optimization distinct

在Postgres数据库中,我正在查询具有3亿行的大型表中MY_DATE的不同值。其中大约有400个,并且对列MY_DATE进行了索引。

Select distinct  MY_DATE from MY_TABLE;

查询运行 22分钟

在Oracle数据库上具有完全相同的数据集和相同的索引定义的相同查询运行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分钟的时间。

有什么建议可以引起问题,我该怎么办?

1 个答案:

答案 0 :(得分:4)

  

不同的值... 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输出中看到了

Index Only Scan ... rows=298788038

建议的新查询通过读取400个索引元组(每个不同的值一个)达到相同的目的。 差异。

使用您测试过的LIMIT(并且没有ORDER BY!),只要检索到足够的行,Postgres就会停止。增加限制会产生 linear 效果。但是,如果每个不同值的行数可以变化,那么增加的成本也将变化。