分区修剪在Postgres 11中是否适用于Update语句

时间:2018-11-28 10:03:55

标签: postgresql database-partitioning

我最近开始使用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正在使用索引来查找必要的分区。

这是好行为吗?你有什么想法吗?

1 个答案:

答案 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可以使用修剪(假设您使用LISTRANGE分区(不适用于PostgreSQL 11中新增的HASH

编辑2:如评论中所述,这似乎与OP在where子句中使用to_date函数有关,该表达式使得与分区键进行比较的表达式成为不可变的表达式。就PostgreSQL而言。这意味着计划者无法对其执行修剪,尽管执行者可以执行,因为它仍然是一个稳定的表达式。就像我上面说的,UPDATE不支持执行者修剪。