【问题标题】:PostgreSQL 13: Efficient use of partitions and tsrangePostgreSQL 13:高效使用分区和 tsrange
【发布时间】:2021-02-07 07:27:08
【问题描述】:

我试图弄清楚如何最有效地使用表分区和 tsrange 函数。我正在使用 PostgreSQL 13。

我将在此处发布一个示例,知道它效率不高并理解原因,但我正在寻找有关如何使其高效和调整的建议。 在下面的示例中,每次运行选择查询时都会读取所有分区。 我想查看预订日历中的游船行程是否与另一行程重叠。

CREATE TABLE boat_trips (
   id INTEGER NOT NULL
   , boat_name VARCHAR(32)
   , departure_time TIMESTAMP WITHOUT TIME ZONE  NOT NULL
   , destination_time TIMESTAMP WITHOUT TIME ZONE NOT NULL
) 
PARTITION BY RANGE (departure_time);
                          
CREATE TABLE IF NOT EXISTS boat_trips_20210206 PARTITION OF boat_trips FOR VALUES FROM ('2021-02-06 00:00:00') TO ('2021-02-06 23:59:59');
CREATE TABLE IF NOT EXISTS boat_trips_20210207 PARTITION OF boat_trips FOR VALUES FROM ('2021-02-07 00:00:00') TO ('2021-02-07 23:59:59');
CREATE TABLE IF NOT EXISTS boat_trips_20210208 PARTITION OF boat_trips FOR VALUES FROM ('2021-02-08 00:00:00') TO ('2021-02-08 23:59:59');
                           
INSERT INTO boat_trips VALUES (1, 'The Beautiful', '2021-02-06 11:15:00'::TIMESTAMP, '2021-02-06 12:15:00'::TIMESTAMP);
INSERT INTO boat_trips VALUES (2, 'The Incredible', '2021-02-06 13:15:00'::TIMESTAMP, '2021-02-06 14:15:00'::TIMESTAMP);
INSERT INTO boat_trips VALUES (3, 'The Beautiful', '2021-02-06 12:30:00'::TIMESTAMP, '2021-02-06 13:15:00'::TIMESTAMP);
INSERT INTO boat_trips VALUES (4, 'The Beautiful', '2021-02-07 11:15:00'::TIMESTAMP, '2021-02-07 12:15:00'::TIMESTAMP);
INSERT INTO boat_trips VALUES (5, 'The Incredible', '2021-02-07 13:15:00'::TIMESTAMP, '2021-02-07 14:15:00'::TIMESTAMP);
INSERT INTO boat_trips VALUES (6, 'The Beautiful', '2021-02-07 12:30:00'::TIMESTAMP, '2021-02-07 13:15:00'::TIMESTAMP);
INSERT INTO boat_trips VALUES (7, 'The Beautiful', '2021-02-08 11:15:00'::TIMESTAMP, '2021-02-08 12:15:00'::TIMESTAMP);
INSERT INTO boat_trips VALUES (8, 'The Incredible', '2021-02-08 13:15:00'::TIMESTAMP, '2021-02-08 14:15:00'::TIMESTAMP);
INSERT INTO boat_trips VALUES (9, 'The Beautiful', '2021-02-08 12:30:00'::TIMESTAMP, '2021-02-08 13:15:00'::TIMESTAMP);

然后我按如下方式运行选择查询:

SELECT DISTINCT bt.id, bt.boat_name, bt.departure_time, bt.destination_time
FROM  boat_trips bt
WHERE  tsrange(departure_time, destination_time) &&
       tsrange '[2021-02-07 00:00:00,2021-02-08 00:00:00)';

如果我得到解释计划,我发现所有的分区都被解析了,这是正常的,因为分区键id是department_time。

如何在我的选择查询中利用分区和tsrange 函数?

我尝试在 tsrange(departure_time, destination_time) 上进行分区,它有效,但我找不到创建分区的语法。

这里有一个例子 => db fiddle

【问题讨论】:

  • 为什么要实现分区?这主要不是性能问题,而是更多“数据管理”工具,例如快速删除旧行(通过删除分区而不是删除数百万行)。您希望该表中有多少行?为什么非分区表不能与 tsrange 上的索引一起使用?
  • 这是因为在一天结束时,表将包含几百万行,并且查询主要是查询当天或将来的几天。但出于报告目的,我们需要保持历史可用。
  • “几百万行”不足以证明分区的合理性。如今,这被认为是一张小桌子。
  • 我理解你的观点,你在某些方面是对的。我习惯于管理具有超过十亿行的表的数据库。这不是关于“我的比你的大”,而是还要考虑索引的大小并避免使用庞大索引的庞大表。分区不会受到伤害,尤其是对于相对“大”的表。顺便说一句,在讨论磁盘空间时,行数是无关紧要的。列的数量及其内容(blob?)呢?因此,我的示例针对该问题进行了简化。实际上,我不使用 3 根柱子和船 ;)
  • FOR VALUES FROM ('2021-02-06 00:00:00') TO ('2021-02-06 23:59:59'); 结束值应该与下一个分区的起始值相同,因为它是用

标签: sql postgresql partitioning


【解决方案1】:

对于像整数这样的标量值,有一个order relationship,所以如果你有一个极限值L,你可以决定任何值x=L进入分区bar。

范围没有可用于进行分区的顺序关系...

假设您有范围 [x1,x2] 和 [y1,y2],为了将它们排序到分区中,您必须对它们进行排序,这意味着定义一个具有所需属性的运算符“

  • a ≤ a(自反性)
  • 如果 a ≤ b 且 b ≤ a 则 a = b(反对称)
  • 如果 a ≤ b 且 b ≤ c 则 a ≤ c(传递性)。

Postgres 有这样一个范围运算符,如果你在一个范围上创建一个 btree 索引,它就是这样使用的。没有订单操作就无法制作btree。但是这个运算符不会产生对范围有意义的顺序,因为对于范围 [x1,x2]

如果您有一组不重叠的范围,那么您可以定义适当的顺序关系。但是您的游船出发和到达时间不是不重叠的集合

这意味着,无论分区之间的限制值L如何,前一个分区中可能存在一个范围,其长度足以使其与下一个分区中的范围重叠。

但是,如果您知道旅行时间不会超过一个月……那么间隔时间不会超过一个月。所以如果你按月分区,你知道分区 N-1 中的某些范围可能与分区 N 中的范围重叠,但分区 N-1 中的范围不会与分区 N+1 中的任何范围重叠。

所以,如果你对每个分区进行约束,以确保每个范围的上限不能超过某个值,就像这样:

CHECK( destination_time < ... )

然后您可以将其添加到您的查询中:

WHERE tsrange(departure_time, destination_time) && tsrange '[2021-02-07 00:00:00,2021-02-08 00:00:00)' AND 目的地时间

约束排除应该消除不需要扫描的分区。

请注意,使用 GIST 索引可以极大地加速范围重叠测试。如果您使用分区的唯一原因是性能,那么这可能是一个更好的解决方案。如果您想使用分区来管理大量数据并加快删除速度,那么最好只使用几个大分区和一个 gist 索引。范围查询将命中所有分区上的 gist 索引,但如果其中没有匹配范围,则每个分区所花费的时间不应超过几十微秒。

【讨论】:

  • 非常感谢您清晰准确的回答。这是我一直在寻找的。我已将您关于 GIST 索引的评论考虑在内,它显着提高了性能。
猜你喜欢
  • 2022-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-13
  • 2021-10-31
相关资源
最近更新 更多