postgres如何决定是使用index scan还是seq scan?

How does postgres decide whether to use index scan or seq scan?

explain analyze 显示 postgres 将对我的查询使用索引扫描,该查询获取行并按日期执行过滤(即 2017-04-14 05:27:51.039):

explain analyze select * from tbl t where updated > '2017-04-14 05:27:51.039';
                                                          QUERY PLAN                                                          
 -----------------------------------------------------------------------------------------------------------------------------
  Index Scan using updated on tbl t  (cost=0.43..7317.12 rows=10418 width=93) (actual time=0.011..0.515 rows=1179 loops=1)
    Index Cond: (updated > '2017-04-14 05:27:51.039'::timestamp without time zone)
  Planning time: 0.102 ms
  Execution time: 0.720 ms

然而 运行使用不同的日期过滤器 '2016-04-14 05:27:51.039' 显示 postgres 将 运行 使用 seq 扫描代替查询:

explain analyze select * from tbl t where updated > '2016-04-14 05:27:51.039';
                                                      QUERY PLAN                                                       
-----------------------------------------------------------------------------------------------------------------------
 Seq Scan on tbl t  (cost=0.00..176103.94 rows=5936959 width=93) (actual time=0.008..2005.455 rows=5871963 loops=1)
   Filter: (updated > '2016-04-14 05:27:51.039'::timestamp without time zone)
   Rows Removed by Filter: 947
 Planning time: 0.100 ms
 Execution time: 2910.086 ms

postgres 如何决定使用什么,特别是在按日期执行过滤时?

Postgres 查询规划器的决策基于成本估算和列统计信息,这些信息由 ANALYZE 收集并由其他一些实用程序命令随机收集。当 autovacuum 开启时(默认情况下),这一切都会自动发生。

The manual:

Most queries retrieve only a fraction of the rows in a table, due to WHERE clauses that restrict the rows to be examined. The planner thus needs to make an estimate of the selectivity of WHERE clauses, that is, the fraction of rows that match each condition in the WHERE clause. The information used for this task is stored in the pg_statistic system catalog. Entries in pg_statistic are updated by the ANALYZE and VACUUM ANALYZE commands, and are always approximate even when freshly updated.

有行计数(在 pg_class 中)、最常见值的列表等。

Postgres 期望找到的行越多,它就越有可能切换到顺序扫描,这样检索 table.

的大部分内容成本更低

一般是索引扫描->位图索引扫描->顺序扫描,越多的行希望被检索到。

对于您的特定示例,重要的统计数据是 histogram_bounds,它让 Postgres 大致了解有多少行具有比给定行更大的值。还有更方便的人眼视角pg_stats

SELECT histogram_bounds
FROM   pg_stats
WHERE  tablename = 'tbl'
AND    attname = 'updated';

有个dedicated chapter explaining row estimation in the manual.

显然,查询的优化很棘手。该答案无意深入探讨 Postgres 优化器的细节。相反,它旨在为您提供一些有关如何做出使用索引的决定的背景知识。

您的第一个查询估计有 return 10,418 行。使用索引时,会发生以下操作:

  • 引擎使用索引查找满足条件的第一个值。
  • 然后引擎循环这些值,当条件不再为真时结束。
  • 对于索引中的每个值,引擎随后在数据页上查找数据。

换句话说,使用索引时会有一点开销——初始化索引,然后单独查找每个数据页。

当引擎进行全面 table 扫描时:

  • 从第一页的第一条记录开始
  • 比较并接受或拒绝记录
  • 按顺序继续遍历所有数据页

没有额外的开销。此外,引擎可以在处理当前页面的同时"pre-load"要扫描的下一页。 I/O 和处理的这种重叠是一个巨大的胜利。

我想说的是,在这两者之间取得平衡可能很棘手。在 10,418 和 5,936,959 之间的某个地方,Postgres 认为索引开销(和随机获取页面)比扫描整个 table.

成本更高