2017-04-15 95 views
1

explain analyze表明Postgres将使用索引扫描我的查询,其获取的行和由日期执行滤波(即,2017-04-14 05:27:51.039):postgres如何决定是使用索引扫描还是seq扫描?

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年4月14日05:27:51.039' 显示,Postgres将使用了序列扫描,而不是运行查询:

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的决定使用什么,特别是?

回答

2

Postgres查询规划师根据成本估算和表格统计信息做出决策。表统计信息由ANALYZE收集,并通过一些其他实用程序命令进行机会性收集。这一切都会在autovacuum开启时自动发生(默认情况下)。

The manual:

大部分查询只检索行的一小部分在一个表中,由于 WHERE条款,限制要检查的行。因此,规划者 需要估计WHERE条款的选择性,即 是与条款中的每个条件匹配的行的分数。用于此任务的信息存储在系统目录中的 pg_statistic中。 pg_statistic中的条目通过 更新为ANALYZEVACUUM ANALYZE命令,并且即使在新鲜更新时也总是近似为 。

有一个行数(在pg_class),最常见的值的列表等

的多个行的Postgres期望找到,就越有可能会切换到一个顺序扫描,这是更便宜地检索大部分表格。

通常,它是索引扫描 - >位图索引扫描 - >顺序扫描,预计检索的行越多。

对于您的特定示例,重要的统计信息是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.

1

显然,查询优化是棘手的。这个答案不打算深入Postgres优化器的细节。相反,它的目的是给你一些关于如何使用索引的决定的背景知识。

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

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

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

当发动机执行全表扫描一下:

  • 开始以第一名的成绩第一个页面
  • 是否比较和接受或通过所有数据页拒绝该记录
  • 顺序继续进行

没有额外的开销。此外,引擎可以在处理当前页面的同时“预加载”下一个要扫描的页面。 I/O和处理的重叠是一个巨大的胜利。

我试图做的一点是获得这两者之间的平衡可能会非常棘手。在10,418到5,936,959之间,Postgres决定索引开销(随机读取页面)花费的不仅仅是扫描整个表格。