为什么SELECT计数(PK)和SELECT计数(*)都这么慢?

时间:2015-05-19 19:48:01

标签: sql postgresql select postgresql-9.3

我有一个名为id的单列PRIMARY KEY的简单表,类型serial。那里有100,000,000行。表占用48GB,PK指数约为2,1GB。机器运行是"专用"仅适用于Postgres,它类似Core i5,500GB HDD,8GB RAM。 Pg config由pgtune实用程序创建(共享缓冲区大约2GB,有效缓存大小为7GB)。操作系统是Ubuntu服务器14.04,Postgres 9.3.6。

为什么SELECT count(id)SELECT count(*)在这个简单的情况下(cca 11分钟)都这么慢?

为什么PostgreSQL规划器选择全表扫描而不是索引扫描应该至少快25倍(在必须从HDD读取整个索引的情况下)。或者我错在哪里?

顺便连续多次运行查询并没有改变任何东西。仍然是11分钟。

此处执行计划:

 Aggregate  (cost=7500001.00..7500001.01 rows=1 width=0) (actual time=698316.978..698316.979 rows=1 loops=1)
   Buffers: shared hit=192 read=6249809
   ->  Seq Scan on transaction  (cost=0.00..7250001.00 rows=100000000 width=0) (actual time=0.009..680594.049 rows=100000001 loops=1)
         Buffers: shared hit=192 read=6249809
 Total runtime: 698317.044 ms

1 个答案:

答案 0 :(得分:8)

考虑到HDD的规格通常介于50Mb / s和100Mb / s之间,那么对于48Gb,我希望读取500到1000之间的所有内容。

由于您没有where子句,计划程序会发现您对大多数记录感兴趣,因此它不使用索引,因为这需要其他索引。 postgresql无法使用索引的原因在于postgresql用于事务一致性的MVCC。这需要拉动行以确保准确的结果。 (见https://wiki.postgresql.org/wiki/Slow_Counting

缓存,CPU等不会影响这一点,也不会更改缓存设置。这是IO绑定的,查询后缓存将被完全删除。

如果您可以使用近似值,则可以使用表元数据中的reltuples字段:

SELECT reltuples FROM pg_class WHERE relname = 'tbl';

由于这只是一行,因此速度极快。

更新:自9.2以来,存储可见性信息的新方法允许仅发生索引计数。但是有一些警告,特别是在没有谓词来限制行的情况下。有关详细信息,请参阅https://wiki.postgresql.org/wiki/Index-only_scans