Jak*_*sel 7 postgresql index statistics execution-plan
在 PostgreSQL 9.3 中,我试图在一个很少使用的(占总记录的 0.00001%)布尔列上创建一个有效的索引。为此,我在 SO 上发现了这篇文章: https //stackoverflow.com/a/12026593/808921
我正在尝试利用 Erwin Brandstetter 推荐的 PostgreSQL 的“部分索引”功能。我已经有一个包含几百万条记录的表,我想将索引添加到该表中,如下所示:
CREATE INDEX schema_defs_deprovision ON schema_defs (deprovision)
WHERE deprovision = 0;
Run Code Online (Sandbox Code Playgroud)
(绝大多数记录都会有 deprovision = 1
)
问题是,当我尝试将此索引与最简单的查询一起使用时,PostgreSQL 就好像它不存在一样:
explain select * from schema_defs where deprovision = 0;
Seq Scan on schema_defs (cost=0.00..1.05 rows=1 width=278)
Filter: (deprovision = 0)
Run Code Online (Sandbox Code Playgroud)
真正奇怪的是,我发现如果这个索引是在表中有数据之前创建的,那么它确实可以正常工作。不相信我?以下是一些证明这一点的 SQL Fiddle 条目:
插入后创建的部分索引(索引不起作用)
插入前创建的部分索引(索引正常工作)
在这两个中,只需展开“查看执行计划”链接即可查看我在说什么。
所以,我的问题是 - 我必须做什么才能让 PostgreSQL 在创建索引之前开始在其中包含数据的表上使用部分索引?
顺便说一句,我也是 SQL Fiddle 的开发人员,这个问题与我正在为此进行的一项新开发工作有关。
ANALYZE
添加索引后运行。并确保该列deprovision
具有统计信息。如何验证?
基本统计pg_class
:
SELECT relname, relkind, reltuples, relpages
FROM pg_class
WHERE oid = 'schema_defs'::regclass;
Run Code Online (Sandbox Code Playgroud)
pg_stats
( pg_statistics
) 中每列的数据直方图:
SELECT attname, inherited, n_distinct
, array_to_string(most_common_vals, E'\n') AS most_common_vals
FROM pg_stats
WHERE tablename = 'schema_defs'
AND attname = 'deprovision';
Run Code Online (Sandbox Code Playgroud)
PostgreSQL 查询计划器依靠有关表内容的统计信息来生成良好的查询计划。这些统计信息由
ANALYZE
命令收集,可以由命令本身调用,也可以作为VACUUM
. 拥有合理准确的统计数据很重要,否则计划的错误选择可能会降低数据库性能。如果启用了 autovacuum 守护程序,则
ANALYZE
只要表的内容发生充分变化,就会自动发出命令。但是,管理员可能更喜欢依赖手动调度的ANALYZE
操作,尤其是在已知表上的更新活动不会影响“感兴趣”列的统计信息的情况下。守护进程ANALYZE
严格按照插入或更新的行数进行调度;它不知道这是否会导致有意义的统计变化。
在您的情况下,仅分析一列就可以完成这项工作:
ANALYZE table_name (deprovision);
Run Code Online (Sandbox Code Playgroud)
在此期间,在 column 上设置索引是没有意义的deprovision
。给定谓词,WHERE deprovision = 0
它不携带附加信息。您也可以使用常量表达式:
CREATE INDEX schema_defs_deprovision ON schema_defs ((true))
WHERE deprovision = 0;
Run Code Online (Sandbox Code Playgroud)
只是一个概念证明。这将不再有用。在这种特殊情况下,您根本不需要索引列,但您必须至少提供一个列或表达式。因此,请使用主键(因为它不会更改并且无论如何都已编入索引,因此不会引入更多限制/开销成本)或任何其他可能对查询有用的小列(<= 8 字节)。
CREATE INDEX schema_defs_deprovision ON schema_defs (id)
WHERE deprovision = 0;
Run Code Online (Sandbox Code Playgroud)
演示小提琴具有误导性。
插入前创建的部分索引(索引正常工作)
您的演示表只有 4 行。Postgres不应该使用索引。类似的问题,正好相反。Postgres 在创建后不会立即对表进行统计——直到第一次运行ANALYZE
. 然后它知道只有 4 行并且不会再接触索引。
那么为什么它在你的第二个演示中正常工作呢?手册:
出于效率原因,
reltuples
并且relpages
不会即时更新,因此它们通常包含有些过时的值。它们由VACUUM
、ANALYZE
和一些 DDL 命令更新,例如CREATE INDEX
.
大胆强调我的。如果在插入行后创建索引,pg_class
则更新那些基本统计信息。但只有那些,而不是 中的详细统计数据pg_statistic
:
in
pg_statistic
中的条目由ANALYZE
和VACUUM ANALYZE
命令更新,并且即使在新更新时也始终是近似值。
为了让 Postgres 使用部分索引(特别是它的原始形式对其他任何东西都没有用),您还需要数据直方图来pg_statistic
通知查询规划器,这deprovision = 0
实际上是一种罕见的情况,因此使用索引是需要付出代价的。
Autovacuum负责解决这个问题。它调度VACUUM
和ANALYZE
自动。但是在写入表和下一次ANALYZE
运行之间有一个时间范围(取决于设置和负载)。如果您在创建表或更改表后立即运行查询,这些最新的更改还不会反映在统计信息中。没关系,如果这不会以相关的方式改变统计数据。如果确实如此,例如在创建大INSERT
表之后或立即创建表之后,请ANALYZE
手动运行以获得正确的查询计划。
请注意,autovacuum 根本不涵盖临时表。如果需要,您总是需要ANALYZE
手动运行这些:
我不知道您如何配置 autovacuum 以及是否/何时运行 ANALYZE
手动。但我过去注意到 sqlfiddle 可能会由于缺少/过时的统计数据而产生误导。
我会非常感兴趣如何ANALYZE
在 sqlfiddle 的幕后处理。最好不要做任何特别的事情,但欢迎提供一些信息。也许每个可用的 RDBMS 版本都有一个基本网页?
我创建了一个SQL小提琴展示的效果CREATE INDEX
和ANALYZE
对各种统计数据。
效果(至少)在我的第一次运行中显示出来。在以后的运行中可能无法重现,您必须创建一个新模式并再次运行。
首先,我们在 中没有看到基本统计数据pg_class
:
relname reltuples relpages
schema_defs 0 0
Run Code Online (Sandbox Code Playgroud)
也不对任何条目deprovision
中pg_statistics
的所有(无结果)。
Postgres 不知道表中有什么并且默认使用索引 - 这是一个糟糕的选择!
之后CREATE INDEX
,我们看到了基本的统计数据,但在 中仍然没有数据直方图pg_statistics
。
在ANALYZE
我们看到两者之后。
有了适当的统计信息,Postgres 现在使用顺序扫描(不错的选择,即使有索引 - 如此少的行会更昂贵)。
归档时间: |
|
查看次数: |
964 次 |
最近记录: |