立即开始为您的 PostgreSQL 增压。
你们都知道这种感觉:你的数据库中有一个很大的表,而且它越来越慢。你的应用程序遇到了瓶颈;用户体验急剧下降。这不是什么好事。
当你不断地将数据流式传输到 PostgreSQL 时,你迟早会得到这些又大又慢的表。幸运的是,PostgreSQL 生态系统提供了一系列 数据分区 技术,以优化这些数据集的性能和维护。在这些分区方法中,有两个最受欢迎:Timescale 的超级表(针对基于时间/范围的分区进行了优化)和pg_partman 扩展。
虽然这两种方法都旨在简化分区,但本文探讨了为什么我们认为 Timescale 的超级表比 pg_partman 具有明显的优势。
📑 查看我们之前关于何时考虑分区的文章—如果您还没有这样做的话。
分区表由许多不重叠的分区组成,每个分区覆盖数据集的一部分。当您使用带有基于时间的限制的 WHERE
子句从分区表中选择数据时,PostgreSQL 能够在计划查询之前立即丢弃所有不相关的分区。
因为我们没有搜索所有数据,所以我们花费更少的时间进行 I/O,并且查询速度更快。如果未分区表的总表大小或(更糟糕的是)总索引大小超过 Postgres 用于缓存的内存量,则差异会变得更加显着。
正如我们在之前关于分区的文章中介绍的那样,您可以遵循不同的策略和技术对 PostgreSQL 表进行分区。就分区类型而言,您可以选择
范围分区:分区由值范围定义(例如,按月、年或递增序列)。
列表分区:分区由值列表定义(例如,按国家/地区)。
哈希分区:行根据分区键的哈希值进行分区,以将数据均匀分布在固定数量的分区中。
根据您使用的分区策略,您可以选择不同的方法,最常见的方法如下
使用 PostgreSQL 中的原生 PARTITION BY
子句。这支持三种类型的分区(例如,PARTITION BY RANGE
、BY LIST
或 BY HASH
)。
使用 pg_partman,这是一个在 PostgreSQL 中自动进行基于时间的分区的扩展。
使用 Timescale,它比 pg_partman 更进一步,通过超级表的概念自动按时间进行分区。
在这里,我们将特别关注范围分区(到目前为止最常见的),比较最后两种方法:pg_partman 和超级表。
用于 PostgreSQL 的pg_partman 扩展建立在 PostgreSQL 对分区表的原生声明性方法之上。PostgreSQL 10 中引入的声明性分区取代了旧的表继承方法,通过为分区提供内置支持(无需触发器或规则)引入了一种更直观、更简单的方法。
使用声明性分区,大部分分区管理都是自动化的,但例如,创建新分区仍然需要手动干预—除非您使用 pg_partman 之类的工具。Pg_partman 帮助通过 SQL API 自动创建和管理分区表和分区。尽管不会自动添加和删除新分区,但可以通过添加另一个扩展来管理,例如pg_cron来安排作业。
如果没有 pg_partman,声明性分区会复杂得多。Ppg_partman 旨在简化此过程,而且确实如此,但仍然有一些重要任务和细微差别需要手动干预。以下是一些例子
在提取数据时,必须确保已创建必要的分区,以避免出现“未找到行的关系分区”错误,这可能会阻止您的写入。
如果您的工作负载涉及零星或不规则的数据提取,则需要确保您没有创建过多不必要的分区,因为它们可能会降低查询性能并导致表膨胀。
您必须确保分区之间没有间隙或重叠,尤其是在处理手动分区修改时。
如果要实施保留策略以定期删除旧分区,则需要进行设置。
如果需要更改表的架构,例如添加或删除列,您通常必须手动处理这些更改,以确保它们正确传播到所有分区。
如果 pg_partman 简化了分区管理,那么超级表则将这种简化提升到了一个新的水平:它们完全自动化了这个过程。如果说 pg_partman 是一般的工具包,那么超级表就是产品。
超表 是一个抽象层,允许您自动创建和管理分区(在 Timescale 中称为块),而不会失去使用 SQL 进行正常查询的能力。超表针对基于时间的分区进行了优化,尽管它们也适用于不基于时间但具有类似内容的表,例如 BIGINT 主键。
超表基于继承分区(您会记得这是 PostgreSQL 使用的旧方法)。虽然这种方法难以手动实现,但它也更加灵活,可以对分区进行更精细的控制。这绝对不是您(作为分区的最终用户)想要设置和管理的东西,但这种灵活性使我们 (Timescale) 能够在原生 PostgreSQL 分区的基础上引入一些改进,您可以直接从中受益。
这些改进是什么?让我们来介绍一下。
使用单个命令 (create_hypertable
) 可以将普通表转换为 Timescale 超表
CREATE TABLE conditions (
time TIMESTAMPTZ NOT NULL,
location TEXT NOT NULL,
device TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL
);
SELECT create_hypertable('conditions', 'time');
这将设置分区列、分区间隔(默认为 7 天)和唯一索引以支持分区。创建超表后,将在数据流入超表时动态创建新分区(块)。
正如我们之前所说,pg_partman 可以自动执行大部分分区创建过程,但要定期安排此自动化,您需要将其与 pg_cron 集成 - 并且您必须确保必要的预先分区到位。如果没有预定义的分区来托管传入数据,您将遇到 找不到行的关系分区 错误。(这是一个常见错误。)
使用 Timescale 消除了分区不存在的风险,完全从数据库所有者需要考虑的事项列表中删除了分区管理。您可以在需要时获得恰当数量的分区。
超表的另一个隐藏优点是它们永远不会创建不必要的分区。分区是动态生成的,这意味着如果没有适合潜在分区的数据,则根本不会创建该分区。这是一件好事,因为每个活动分区在查询计划期间都会增加轻微的开销。
正如我们在这篇文章中广泛介绍的那样, PostgreSQL 中的 DDL 操作(例如添加新分区)本质上需要对表进行独占锁定。这意味着在执行操作的短暂时间内,尝试写入(插入、更新、删除)到表的其他事务可能会被阻塞,直到操作完成。
当 pg_partman 为其维护作业创建这些分区时,它会在表上执行 DDL 操作。这些操作需要独占锁定,这可能会完全阻塞写入。还可能会出现其他问题:事务的等待时间可能会增加,导致响应时间不可预测地变长,并且在操作具有严格超时限制的系统中,由锁定引起的等待可能会导致操作失败。
超表旨在确保您的应用程序的读取或写入操作不会中断。Timescale 维护着自己的分区目录,并实施了自己的最小化锁定策略,允许读取和写入而不干扰添加或删除分区。
对数据进行分区的好处之一是您可以立即删除单个分区,而这在编写大型 DELETE
语句时则不然。
使用 pg_partman 时,您需要自己创建用于删除旧分区的自定义逻辑,并且删除分区将锁定主表。此外,您需要使用 pg_cron 或外部调度程序安排此操作。
相反,为超表设置自动数据保留策略很简单:您不需要额外的代码或管理更多扩展。只需一个命令 add_retention_policy
。您可以为特定时间间隔定义保留期,Timescale 会在需要时自动删除过时的分区。
SELECT add_retention_policy('conditions', INTERVAL '24 hours');
超表还解锁了 Timescale 为您的查询计划启用的一些额外功能。例如,在修剪分区时引用 now()
的查询将执行得更好,因为 now()
被转换为常量, 并且您的有序 DISTINCT
查询将受益于 SkipScan。
值得注意的是,虽然 pg_partman 更像是 PostgreSQL 的通用分区管理器,但超表解锁了大量专为基于时间(或时间序列)数据量身定制的功能,这些功能对于扩展大型 PostgreSQL 表非常方便
Timescale 压缩 采用超表并将其从面向行更改为面向列。这可以将存储利用率降低高达 95%,解锁极速的分析查询, 并且仍然允许数据就地更新。
连续聚合 采用超表,并允许您为聚合查询创建增量更新的物化视图。您可以定义查询并获取一个聚合表,该表会随着历史数据的变化而更新,同时还会随着实时数据的传入而保持更新。
超函数 为您提供了一套完整的函数、过程和数据类型,这些函数、过程和数据类型经过优化,可用于查询、聚合和 分析时间序列数据。
Timescale 作业调度程序 允许您在 PostgreSQL 中调度任何基于 SQL 或函数的作业,这意味着您不需要外部调度程序或加载其他扩展程序,如 pg_cron。
Pg_partman 是一个很棒的工具包,它极大地简化了 PostgreSQL 中声明性分区的管理,但它只是一个工具包。
我们认为,超表是一个完整的产品,它使分区更加精简。动态分区管理、减少的锁定开销和自动保留策略使超表成为处理大型数据集的应用程序的更好选择。您将节省时间和精力,并且您将解锁许多其他惊人的功能,这些功能将使处理大型 PostgreSQL 表变得更加容易。