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

Ala*_*met 7 postgresql indexing performance 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);
Run Code Online (Sandbox Code Playgroud)

A,B和D执行索引扫描.C_LargeTable使用a 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')
...
Run Code Online (Sandbox Code Playgroud)

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

这可能是我在C_LargeTable中组织数据时可能需要执行的优化的指示吗?

Cra*_*ger 11

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

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

请确保您运行9.2或更新版本,并且自动清理配置为运行频率在桌子上.然后,您应该能够在使用可见性图的位置进行仅索引扫描.这只适用于马注意到的有限情况; 在计数仅索引扫描查看维基页面.如果你不让autovacuum定期运行,可见性图将会过时,Pg将无法进行仅索引扫描.

将来,请确保您发布explain或最好explain analyze输出任何查询.