PostgreSql:具有相同列的不同查询计划

时间:2010-07-01 11:25:39

标签: postgresql sql-execution-plan

我有一个包含2个外键的表,我们称之为fk1和fk2。两者都有相同的类型和相同的索引。但是当我“解释”一个简单的选择查询时,我得到完全不同的查询计划。对于fk1:

explain select * from mytable where fk1 = 1;

结果

Index Scan using fk1_idx on mytable  (cost=0.00..9.32 rows=2 width=4)
  Index Cond: (fk1 = 1)

对于fk2

explain select * from mytable where fk2 = 1;

结果:

Bitmap Heap Scan on mytable  (cost=5.88..659.18 rows=208 width=4)
  Recheck Cond: (fk2 = 1)
  ->  Bitmap Index Scan on fk2_idx  (cost=0.00..5.83 rows=208 width=0)
        Index Cond: (fk2 = 1)

第二个似乎效率更低。是否因为它可能会返回更多结果,因此更复杂的查询得到回报?

2 个答案:

答案 0 :(得分:6)

是的,这归结为谓词的“选择性”(“where ...”子句)。

如果谓词只选择表中的一小部分行,那么通过对表数据的任意排序访问来单独获取每个行是有意义的,因为只会提取几页。

随着要选择的行数增加,位图扫描变得更加合适:索引用于确定表中的哪些页面是“有趣的”,然后按照它们放置在页面中的顺序扫描这些页面。表数据文件。这具有可以一起请求相邻页面的优点,这可能由文件系统/磁盘系统更有效地服务。 (当然,这取决于表数据文件是否合理地未分段)。由于不保留每个页面中的实际有趣元组集合,只是页面集合本身,必须为检索到的页面中的每个元组重新评估谓词:因此查询中的“重新检查cond”。 (这种策略的一个优点是它允许组合多个独立索引中的查找,只需将几个索引查找中的“有趣页面”位图与AND(或ORing)组合在一起)

随着要选择的行数进一步增长,扫描索引的优势逐渐减少,因为可能的结果是大多数表将被标记为“有趣”。因此,最终一个简单的顺序扫描变得合适:所有页面都按顺序遍历,并且忽略索引。

IIRC这可能会导致请求不到15%的表可能是索引扫描,15-50%是位图扫描,50%+是seq扫描。非常粗略。这受到random_page_cost和seq_page_cost等相关设置的影响很大(例如effective_cache_size)。

Postgresql收集有关公共值及其频率的统计信息,以及数据库中每列的其他值的直方图 - 这用于估计选择性并填充您在EXPLAIN输出中看到的“行”估计值。该文档包含如何完成此操作的说明:http://www.postgresql.org/docs/current/static/planner-stats-details.html

答案 1 :(得分:2)

是的,查询计划将基于表中内容的统计数据。记得运行“真空分析”;不时地将统计数据保存到数据中。