Postgres分区?
Postgres partitioning?
我的软件 运行 每 30 分钟执行一次 cronjob,从 Google 分析/社交网络中提取数据并将结果插入 Postgres 数据库。
数据如下所示:
url text NOT NULL,
rangeStart timestamp NOT NULL,
rangeEnd timestamp NOT NULL,
createdAt timestamp DEFAULT now() NOT NULL,
...
(various integer columns)
因为一次查询 returns 10 000 多个项目,将这些数据存储在一个 table 中显然不是一个好主意。按照这个速度,cronjob 每天将生成大约 480,000 条记录,每月生成大约 1450 万条记录。
我认为解决方案是使用多个 table,例如我可以使用特定的 table 来存储给定月份生成的数据:stats_2015_09、stats_2015_10、stats_2015_11等
我知道 Postgres 支持 table 分区。但是,我是这个概念的新手,所以我不确定最好的方法是什么。在这种情况下我需要分区,还是应该手动创建这些 table?或者也许有更好的解决方案?
稍后将以各种方式查询数据,这些查询预计会 运行 快。
编辑:
如果我以 12-14 table 结束,每个存储 10-20 百万行,Postgres 应该仍然能够快速 运行 select 语句,对吗?插入不必非常快。
分区在各种情况下都是一个好主意。想到的两个是:
- 您的查询有一个
WHERE
子句,可以很容易地映射到一个或几个分区。
- 您想要一种快速删除历史数据的方法(删除分区比删除记录更快)。
在不了解您想要运行的查询类型的情况下,很难说分区是否是个好主意。
我想我可以说将数据拆分成不同的 tables 是一个 糟糕的 想法,因为它是维护的噩梦:
- 您不能在 table 中引用外键。
- 跨越多个 table 的查询很麻烦,所以简单的问题很难回答。
- 维护 table 成为一场噩梦(adding/removing 专栏)。
- 如果您的用户具有不同的角色,则必须谨慎维护权限。
无论如何,开始的地方是 Postgres 关于分区的文档,即 here。我应该注意到 Postgres 的实现比其他数据库更笨拙,因此您可能需要查看 MySQL 或 SQL 服务器的文档以了解它在做什么。
首先,我想挑战一下你提问的前提:
Since one query returns 10 000+ items, it's obviously not a good idea to store this data in a single table.
据我所知,数据库不能很好地处理数百万行的单个 table 没有根本原因。在极端情况下,如果您创建了一个没有索引的 table,并简单地向其追加行,Postgres 可以简单地继续将这些行写入磁盘,直到您 运行 存储空间不足 space。 (内部可能还有其他限制,我不确定;但如果是这样,它们是 大。)
只有当您尝试对这些数据做某事时才会出现问题,而确切的问题 - 因此确切的解决方案 - 取决于 什么 你做的。
如果你想定期删除超过固定时间刻度之前插入的所有行,你可以在 createdAt
列上对数据进行分区。然后 DELETE
将成为一个非常高效的 DROP TABLE
,并且所有 INSERT
将通过触发器路由到 "current" 分区(或者甚至可以绕过它,如果你导入脚本知道分区命名方案)。然而,SELECT
s 可能无法在其 WHERE
子句中指定 运行ge of createAt
值,因此需要查询所有分区并将结果。一次保留的分区越多,效率就越低。
或者,您可以检查 table 上的工作负载,并发现所有查询都已经明确声明了 rangeStart
值,或者可以很容易地明确声明该值。在这种情况下,您可以在 rangeStart
上进行分区,查询规划器在规划每个 SELECT
查询时将能够消除除一个或几个分区之外的所有分区。 INSERT
需要通过触发器路由到适当的 table,维护操作(例如删除不再需要的旧数据)的效率会低得多。
或者您可能知道一旦 rangeEnd
变为 "too old" 您将不再需要数据,并且可以获得两个好处:按 rangeEnd
分区,确保您的所有 SELECT
查询明确提及 rangeEnd
,并删除包含您不再感兴趣的数据的分区。
借用Linus Torvald的术语git,分区的"plumbing"以table继承的形式内置到Postgres中,as documented here, but there is little in the way of "porcelain" other than examples in the manual. However, there is a very good extension called pg_partman提供管理功能基于 ID 或日期 运行ges 的分区集;阅读文档以了解不同的操作模式是非常值得的。就我而言,none 非常匹配,但是分叉该扩展比从头开始编写所有内容要容易得多。
请记住,分区不是免费的,如果基于上述考虑因素没有明显的候选列作为分区依据,实际上最好将数据保留在一个 table ,并考虑其他优化策略。例如,部分索引 (CREATE INDEX ... WHERE
) 可能能够处理最常查询的行子集;可能与 "covering indexes" 结合使用,其中 Postgres 可以 return 直接从索引中查询结果,而无需参考主 table 结构 ("index-only scans").
我的软件 运行 每 30 分钟执行一次 cronjob,从 Google 分析/社交网络中提取数据并将结果插入 Postgres 数据库。
数据如下所示:
url text NOT NULL,
rangeStart timestamp NOT NULL,
rangeEnd timestamp NOT NULL,
createdAt timestamp DEFAULT now() NOT NULL,
...
(various integer columns)
因为一次查询 returns 10 000 多个项目,将这些数据存储在一个 table 中显然不是一个好主意。按照这个速度,cronjob 每天将生成大约 480,000 条记录,每月生成大约 1450 万条记录。
我认为解决方案是使用多个 table,例如我可以使用特定的 table 来存储给定月份生成的数据:stats_2015_09、stats_2015_10、stats_2015_11等
我知道 Postgres 支持 table 分区。但是,我是这个概念的新手,所以我不确定最好的方法是什么。在这种情况下我需要分区,还是应该手动创建这些 table?或者也许有更好的解决方案?
稍后将以各种方式查询数据,这些查询预计会 运行 快。
编辑:
如果我以 12-14 table 结束,每个存储 10-20 百万行,Postgres 应该仍然能够快速 运行 select 语句,对吗?插入不必非常快。
分区在各种情况下都是一个好主意。想到的两个是:
- 您的查询有一个
WHERE
子句,可以很容易地映射到一个或几个分区。 - 您想要一种快速删除历史数据的方法(删除分区比删除记录更快)。
在不了解您想要运行的查询类型的情况下,很难说分区是否是个好主意。
我想我可以说将数据拆分成不同的 tables 是一个 糟糕的 想法,因为它是维护的噩梦:
- 您不能在 table 中引用外键。
- 跨越多个 table 的查询很麻烦,所以简单的问题很难回答。
- 维护 table 成为一场噩梦(adding/removing 专栏)。
- 如果您的用户具有不同的角色,则必须谨慎维护权限。
无论如何,开始的地方是 Postgres 关于分区的文档,即 here。我应该注意到 Postgres 的实现比其他数据库更笨拙,因此您可能需要查看 MySQL 或 SQL 服务器的文档以了解它在做什么。
首先,我想挑战一下你提问的前提:
Since one query returns 10 000+ items, it's obviously not a good idea to store this data in a single table.
据我所知,数据库不能很好地处理数百万行的单个 table 没有根本原因。在极端情况下,如果您创建了一个没有索引的 table,并简单地向其追加行,Postgres 可以简单地继续将这些行写入磁盘,直到您 运行 存储空间不足 space。 (内部可能还有其他限制,我不确定;但如果是这样,它们是 大。)
只有当您尝试对这些数据做某事时才会出现问题,而确切的问题 - 因此确切的解决方案 - 取决于 什么 你做的。
如果你想定期删除超过固定时间刻度之前插入的所有行,你可以在 createdAt
列上对数据进行分区。然后 DELETE
将成为一个非常高效的 DROP TABLE
,并且所有 INSERT
将通过触发器路由到 "current" 分区(或者甚至可以绕过它,如果你导入脚本知道分区命名方案)。然而,SELECT
s 可能无法在其 WHERE
子句中指定 运行ge of createAt
值,因此需要查询所有分区并将结果。一次保留的分区越多,效率就越低。
或者,您可以检查 table 上的工作负载,并发现所有查询都已经明确声明了 rangeStart
值,或者可以很容易地明确声明该值。在这种情况下,您可以在 rangeStart
上进行分区,查询规划器在规划每个 SELECT
查询时将能够消除除一个或几个分区之外的所有分区。 INSERT
需要通过触发器路由到适当的 table,维护操作(例如删除不再需要的旧数据)的效率会低得多。
或者您可能知道一旦 rangeEnd
变为 "too old" 您将不再需要数据,并且可以获得两个好处:按 rangeEnd
分区,确保您的所有 SELECT
查询明确提及 rangeEnd
,并删除包含您不再感兴趣的数据的分区。
借用Linus Torvald的术语git,分区的"plumbing"以table继承的形式内置到Postgres中,as documented here, but there is little in the way of "porcelain" other than examples in the manual. However, there is a very good extension called pg_partman提供管理功能基于 ID 或日期 运行ges 的分区集;阅读文档以了解不同的操作模式是非常值得的。就我而言,none 非常匹配,但是分叉该扩展比从头开始编写所有内容要容易得多。
请记住,分区不是免费的,如果基于上述考虑因素没有明显的候选列作为分区依据,实际上最好将数据保留在一个 table ,并考虑其他优化策略。例如,部分索引 (CREATE INDEX ... WHERE
) 可能能够处理最常查询的行子集;可能与 "covering indexes" 结合使用,其中 Postgres 可以 return 直接从索引中查询结果,而无需参考主 table 结构 ("index-only scans").