这是我的疑问:
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
的索引。我错过了任何索引吗?
PS。我设法通过添加新的idex ON bill(name, amount)
来解决问题。我不明白为什么它有所帮助,所以让我们暂时搁置这个问题......
答案 0 :(得分:6)
由于您要搜索特定名称,因此您应该拥有一个名称为第一列的索引,例如CREATE INDEX IX_bill_name ON bill( name )
。
但是Postgres仍然可以选择进行全表扫描,如果它估计你的索引不够具体,即如果它认为扫描所有行并选择匹配的那些而不是咨询索引并开始跳跃会更快在表中收集匹配的行。 Postgres使用基于成本的估算技术,对随机磁盘读取进行加权比顺序读取更加昂贵。
对于在您的情况下实际使用的索引,应该不超过您搜索的行的10%。由于您的大多数行都有name = peter,因此执行全表扫描实际上会更快。
至于为什么没有过滤的SUM运行得更快,这与表的整体宽度有关。使用where子句,postgres必须按顺序读取表中的所有行,以便忽略那些与过滤器不匹配的行。如果没有where子句,postgres可以读取索引中的所有金额。因为金额索引包含每个相应行的数量和指针,但表中没有其他数据,所以只需要更少的数据。基于性能上的巨大差异,我猜你的表中还有很多其他领域......