索引列的极慢非重复查询

tom*_*zer 4 sql postgresql indexing distinct query-optimization

在Postgres数据库中,我正在查询MY_DATE具有3亿行的大型表中的不同值。它们大约有400个,并且该列MY_DATE已建立索引。

Select distinct  MY_DATE from MY_TABLE;
Run Code Online (Sandbox Code Playgroud)

查询运行22分钟

在Oracle数据库上具有完全相同的数据集和相同的索引定义的相同查询运行11秒。

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

EXPLAIN Select distinct  MY_DATE from MY_TABLE LIMIT 200;
Run Code Online (Sandbox Code Playgroud)

给出:

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)
Run Code Online (Sandbox Code Playgroud)

当我限制结果时,查询会变得更快。例如

Select distinct  MY_DATE from MY_TABLE LIMIT 5;
Run Code Online (Sandbox Code Playgroud)

在不到几秒的时间内运行。

但:

Select distinct  MY_DATE from MY_TABLE LIMIT 50;
Run Code Online (Sandbox Code Playgroud)

已经花了几分钟。该LIMIT子句的时间似乎成倍增加。

我希望Postgres查询能够像OracleDB一样在几秒钟内运行。索引扫描(即使是一张大桌子)也需要20分钟才能完成。

有什么建议导致问题的原因以及我该怎么办?

Erw*_*ter 5

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

很多更快的技术可以做到这一点。模拟一个松散的索引扫描(也称为跳过扫描),并假设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;
Run Code Online (Sandbox Code Playgroud)

有关:

使用您提到的索引应该以毫秒为单位完成。

Oracle DB ... 11秒。

因为Oracle具有本机索引跳过扫描,而Postgres没有。正在不断努力在Postgres 12中实现类似的功能。

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

仅索引扫描... 行= 298788038

建议的新查询通过读取400个索引元组(每个不同的值一个)实现相同的目的。很大的不同。

随着LIMIT(没有ORDER BY像你这样的测试!),Postgres的只要够检索行停止。增加限制具有线性作用。但是,如果每个不同值的行数可以变化,那么增加的成本也将变化。