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

Kou*_*lik 7 sql postgresql select postgresql-9.3

我有一个简单的表,其中单列PRIMARY KEY称为id,type 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(*)在这个简单的例子(CCA11分钟)这么慢?

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

顺便多次连续运行查询并没有改变任何东西.仍然cca 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
Run Code Online (Sandbox Code Playgroud)

Pet*_*ans 8

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

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

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

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

SELECT reltuples FROM pg_class WHERE relname = 'tbl';
Run Code Online (Sandbox Code Playgroud)

由于这只是一排,因此速度极快.

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