postgres没有在SELECT COUNT(*)上使用索引来表示大型表

时间:2013-12-04 21:46:27

标签: performance postgresql indexing sql-execution-plan

我有四张桌子;两个用于当前数据,两个用于存档数据。其中一个归档表有数千万行。所有表都有几个窄索引,非常相似。

鉴于以下问题:

SELECT (SELECT COUNT(*) FROM A)
UNION SELECT (SELECT COUNT(*) FROM B)
UNION SELECT (SELECT COUNT(*) FROM C_LargeTable)
UNION SELECT (SELECT COUNT(*) FROM D);

A,B和D执行索引扫描。 C_LargeTable使用seq scan,查询大约需要20秒才能执行。表D也有数百万行,但只占C_LargeTable

大小的10%

如果我然后修改我的查询以使用以下逻辑执行,这足以缩小计数,我仍然得到相同的结果,使用索引并且查询大约需要5秒,或1/4的时间

...
SELECT (SELECT COUNT(*) FROM C_LargeTable WHERE idx_col < 'G') 
       + (SELECT COUNT(*) FROM C_LargeTable WHERE idx_col BETWEEN 'G' AND 'Q')
       + (SELECT COUNT(*) FROM C_LargeTable WHERE idx_col > 'Q')
...

当存在完全良好的索引并且存在可确保唯一性的覆盖主键时,对于计数具有全表扫描的I / O开销是没有意义的。我对postgres的理解是PRIMARY KEY与SQL Server集群索引不同,它确定了一种排序,但它隐式创建了一个btree索引来确保唯一性,我认为这应该要求I / O少得多。全表扫描。

这是否可能表明我可能需要执行以在C_LargeTable中组织数据?

1 个答案:

答案 0 :(得分:11)

主键没有覆盖索引,因为PostgreSQL不支持它们(无论如何都是真的,包括9.4)。

由于MVCC visibility,因此需要进行堆扫描。索引不包含可见性信息。 Pg可以进行索引扫描,但它仍然必须检查来自堆的可见性信息,并且索引扫描是随机I / O来读取整个表,因此seqscan将更快。

确保您运行9.2或更新版本,并且autovacuumconfigured to run frequently上是the wiki page on count。然后,您应该能够在使用可见性图的位置进行仅索引扫描。这只适用于马注意到的有限情况;见index-only scansvisibility map。如果你不让autovacuum定期运行,{{3}}将会过时,Pg将无法进行仅索引扫描。

将来,请确保您发布explain或最好explain analyze输出以及任何疑问。