2

这是我的查询:

SELECT SUM(amount) FROM bill WHERE name = 'peter'

表中有 800K+ 行。EXPLAIN ANALYZE说:

Aggregate  (cost=288570.06..288570.07 rows=1 width=4) (actual time=537213.327..537213.328 rows=1 loops=1)
->  Seq Scan on bill  (cost=0.00..288320.94 rows=498251 width=4) (actual time=48385.201..535941.041 rows=800947 loops=1)
Filter: ((name)::text = 'peter'::text)
Rows Removed by Filter: 8
Total runtime: 537213.381 ms

所有行都受到影响,这是正确的。但是为什么这么久?类似的查询没有WHERE运行得更快:

ANALYZE EXPLAIN SELECT SUM(amount) FROM bill
Aggregate  (cost=137523.31..137523.31 rows=1 width=4) (actual time=2198.663..2198.664 rows=1 loops=1)
->  Index Only Scan using idx_amount on bill  (cost=0.00..137274.17 rows=498268 width=4) (actual time=0.032..1223.512 rows=800955 loops=1)
Heap Fetches: 533399
Total runtime: 2198.717 ms

我有一个索引amount和一个索引name。我错过了任何索引吗?

附言。我设法通过添加一个新的 idex 来解决这个问题ON bill(name, amount)。我不明白为什么它有帮助,所以让我们把这个问题留待一段时间......

4

1 回答 1

7

由于您正在搜索特定名称,因此您应该有一个以名称作为第一列的索引,例如CREATE INDEX IX_bill_name ON bill( name ).

但是如果 Postgres 估计你的索引不够具体,它仍然可以选择进行全表扫描,即如果它认为只扫描所有行并选择匹配的行而不是查阅索引并开始在表来收集匹配的行。Postgres 使用基于成本的估计技术,将随机磁盘读取加权为比顺序读取更昂贵。

对于在您的情况下实际使用的索引,与您正在搜索的内容匹配的行数不应超过 10%。由于您的大多数行都有 name=peter ,因此进行全表扫描实际上更快。

至于为什么没有过滤的 SUM 运行得更快与表格的整体宽度有关。使用 where 子句,postgres 必须顺序读取表中的所有行,以便它可以忽略那些与过滤器不匹配的行。如果没有 where 子句,postgres 可以改为从索引中读取所有金额。因为金额索引包含金额和指向每个相应行的指针,但没有来自表中的其他数据,所以它只是更少的数据需要遍历。基于性能上的巨大差异,我猜你的表中有很多其他字段..

于 2013-09-11T13:18:57.213 回答