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

Why are both SELECT count(PK) and SELECT count(*) so slow?

我有一个简单的 table 单列 PRIMARY KEY 名为 id,类型 serial。那里正好有 100,000,000 行。 Table 占用 48GB,PK 索引约 2.1GB。机器 运行 on "dedicated" 仅适用于 Postgres,类似于 Core i5、500GB HDD、8GB RAM。 Pg 配置由 pgtune 实用程序创建(共享缓冲区约 2GB,有效缓存约 7GB)。 OS 是 Ubuntu 服务器 14.04,Postgres 9.3.6。

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

为什么 PostgreSQL 规划器选择完全 table 扫描而不是索引扫描,索引扫描应该至少快 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

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

由于您没有 where 子句,规划器发现您对大部分记录感兴趣,因此它不使用索引,因为这将需要额外的索引。 postgresql 不能使用索引的原因在于 postgresql 用于事务一致性的 MVCC。这需要拉取行以确保准确的结果。 (参见 https://wiki.postgresql.org/wiki/Slow_Counting

缓存、CPU 等不会对此产生影响,也不会更改缓存设置。这是 IO 绑定的,缓存将在查询后完全废弃。

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

SELECT reltuples FROM pg_class WHERE relname = 'tbl';

因为这只是一行,所以速度非常快。

更新:从 9.2 开始,一种存储可见性信息的新方法允许仅索引计数发生。然而,有很多注意事项,特别是在没有谓词限制行的情况下。有关详细信息,请参阅 https://wiki.postgresql.org/wiki/Index-only_scans