我有一个名为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
答案 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。