我最近开始使用postgresql 11,遇到一种有趣的行为。
我创建了带有分区的表“转换”(logdate是键)。
CREATE TABLE conversions(
.....
logdate timestamp without time zone,
....
) PARTITION BY RANGE (logdate);
CREATE INDEX ON conversions(logdate);
--
CREATE unique INDEX conversions_log_id_idx ON conversions(logdate, id);
CREATE INDEX conversions_log_is_created_idx ON conversions(logdate, is_created);
分区修剪适用于SELECT语句(如文档中所述):
SELECT *
FROM conversions
WHERE logdate BETWEEN to_date('2017-09-01 00:00:00','YYYY-MM-DD HH24:MI:SS') AND to_date('2017-09-08 23:59:59','YYYY-MM-DD HH24:MI:SS');
"Append (cost=0.42..11134.74 rows=13051 width=1715)"
" Subplans Removed: 12"
" -> Index Scan using conversions_y2017q03_logdate_idx on conversions_y2017q03 (cost=0.42..10962.37 rows=13038 width=1715)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_default_logdate_is_created_idx on conversions_default (cost=0.14..8.16 rows=1 width=2030)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
效果很好,您可以在之前看到它。
但是它不适用于更新语句。
例如:
UPDATE conversions
SET is_created = 'N'
WHERE logdate BETWEEN to_date('2017-09-01 00:00:00','YYYY-MM-DD HH24:MI:SS') AND to_date('2017-09-08 23:59:59','YYYY-MM-DD HH24:MI:SS')
"Update on conversions (cost=0.42..11069.48 rows=13051 width=1727)"
" Update on conversions_y2016q04"
" Update on conversions_y2017q01"
" Update on conversions_y2017q02"
" Update on conversions_y2017q03"
" Update on conversions_y2017q04"
" Update on conversions_y2018q01"
" Update on conversions_y2018q02"
" Update on conversions_y2018q03"
" Update on conversions_y2018q04"
" Update on conversions_y2019q01"
" Update on conversions_y2019q02"
" Update on conversions_y2019q03"
" Update on conversions_y2019q04"
" Update on conversions_default"
" -> Index Scan using conversions_y2016q04_logdate_is_created_idx on conversions_y2016q04 (cost=0.42..8.44 rows=1 width=1661)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2017q01_logdate_is_created_idx on conversions_y2017q01 (cost=0.42..8.45 rows=1 width=1804)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2017q02_logdate_is_created_idx on conversions_y2017q02 (cost=0.42..8.45 rows=1 width=1805)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2017q03_logdate_idx on conversions_y2017q03 (cost=0.42..10962.37 rows=13038 width=1727)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2017q04_logdate_id_idx on conversions_y2017q04 (cost=0.29..8.31 rows=1 width=1699)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2018q01_logdate_is_created_idx on conversions_y2018q01 (cost=0.14..8.16 rows=1 width=2036)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2018q02_logdate_is_created_idx on conversions_y2018q02 (cost=0.14..8.16 rows=1 width=2036)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2018q03_logdate_is_created_idx on conversions_y2018q03 (cost=0.14..8.16 rows=1 width=2036)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2018q04_logdate_is_created_idx on conversions_y2018q04 (cost=0.14..8.16 rows=1 width=2036)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2019q01_logdate_is_created_idx on conversions_y2019q01 (cost=0.14..8.16 rows=1 width=2036)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2019q02_logdate_is_created_idx on conversions_y2019q02 (cost=0.14..8.16 rows=1 width=2036)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2019q03_logdate_is_created_idx on conversions_y2019q03 (cost=0.14..8.16 rows=1 width=2036)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_y2019q04_logdate_is_created_idx on conversions_y2019q04 (cost=0.14..8.16 rows=1 width=2036)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
" -> Index Scan using conversions_default_logdate_is_created_idx on conversions_default (cost=0.14..8.16 rows=1 width=2036)"
" Index Cond: ((logdate >= to_date('2017-09-01 00:00:00'::text, 'YYYY-MM-DD HH24:MI:SS'::text)) AND (logdate <= to_date('2017-09-08 23:59:59'::text, 'YYYY-MM-DD HH24:MI:SS'::text)))"
似乎Postgresql正在使用索引来查找必要的分区。
这是好行为吗?你有什么想法吗?
答案 0 :(得分:0)
您是否正在使用准备好的语句?如果是这样,则可以使用通用计划,这意味着计划者将无法执行修剪,因为执行该计划所需的值仅在执行通用计划时可用。 SELECT
自PostgreSQL 11开始支持执行时修剪,因此即使计划者无法在其情况下进行修剪也可以。但是,对于UPDATE
,只有计划者可以执行修剪,因此如果使用通用计划,则没有任何修剪。我怀疑您可能正在使用准备好的语句,因为查看问题中共享的SELECT
的解释输出:
"Append (cost=0.42..11134.74 rows=13051 width=1715)"
" Subplans Removed: 12"
很明显,正在使用运行时修剪。在此处阅读有关分区修剪的更多信息:
https://www.postgresql.org/docs/devel/ddl-partitioning.html#DDL-PARTITION-PRUNING
编辑:我还要提及的是,如果计划者进行了更改(非通用计划),则UPDATE可以使用修剪(假设您使用LIST
和RANGE
分区(不适用于PostgreSQL 11中新增的HASH
。
编辑2:如评论中所述,这似乎与OP在where子句中使用to_date
函数有关,该表达式使得与分区键进行比较的表达式成为不可变的表达式。就PostgreSQL而言。这意味着计划者无法对其执行修剪,尽管执行者可以执行,因为它仍然是一个稳定的表达式。就像我上面说的,UPDATE不支持执行者修剪。