我有一个具有1M +条记录的表,并且我在列testΣ
上有一个BTREE索引,这也是外键:
x
现在我正在跑步:
id | x
---------------
1 | 209
2 | 210
...
1000000 | 209
这需要250毫秒(非常慢!),这就是我通过SELECT COUNT(id) FROM t WHERE x = 209
所获得的:
EXPLAIN
这是索引:
Finalize Aggregate (cost=20993.19..20993.19 rows=1 width=8)
->; Gather (cost=20993.09..20993.19 rows=1 width=8)
Workers Planned: 1
->; Partial Aggregate (cost=19993.09..19993.09 rows=1 width=8)
->; Parallel Seq Scan on t (cost=0.00..19820.19 rows=345789 width=4)
Filter: (x = 209)
怎么了?
答案 0 :(得分:3)
问题在于,有很多行符合条件,和您算上id
。后者意味着PostgreSQL无法使用仅索引扫描,因为它必须从表中获取id
。这是因为,与大多数聚合函数一样,count()
将仅计算SQL标准指定的NOT NULL
的值。
如果id
不可为空,并且表最近是VACUUM
版(因此可见性图的大多数块都标记为“全部可见”),那么您将更快:
SELECT count(*) FROM t WHERE x = 209;
有关count()
的速度的更多思考,请参见my blog。
答案 1 :(得分:1)
您的代码估计将返回1,000,000条中的345,789行。
基于此估计,Postgres将需要读取所有数据页上的记录。我认为,即使使用覆盖索引,Postgres也始终引用数据页,因为它必须检查各种类型的锁和脏数据。
由于需要读取所有页面,Postgres做出合理的假设,即顺序扫描它们比浏览索引要快。
当索引减少需要读取的数据页数时,它们对于过滤很有用。该查询似乎没有这样做。
编辑:
此问题没有真正的解决方案。您可以创建一个由触发器维护的摘要表。这样会增加insert
/ update
/ delete
的负载,这可能是不希望的。