我的软件每30分钟运行一次cronjob,从Google Analytics / Social网络中提取数据并将结果插入Postgres数据库。
数据如下所示:
url text NOT NULL,
rangeStart timestamp NOT NULL,
rangeEnd timestamp NOT NULL,
createdAt timestamp DEFAULT now() NOT NULL,
...
(various integer columns)
由于一个查询返回10 000多个项目,因此将这些数据存储在单个表中显然不是一个好主意。按此速度,cronjob每天将产生约48万条记录,每月产生约1450万条记录。
我认为解决方案是使用多个表,例如我可以使用特定的表来存储给定月份生成的数据:stats_2015_09,stats_2015_10,stats_2015_11等。
我知道Postgres支持表格分区。但是,我对这个概念不熟悉,所以我不确定最好的方法是什么。在这种情况下我是否需要分区,还是应该手动创建这些表?或者可能有更好的解决方案?
稍后将以各种方式查询数据,并且预计这些查询将快速运行。
编辑:
如果我最终得到12-14个表,每个表存储10到2千万行,Postgres应该仍能快速运行select语句,对吧?插入不必非常快。
答案 0 :(得分:6)
评论太长了。
在各种情况下,分区是一个好主意。想到的两个是:
WHERE
子句,可以很容易地映射到一个或几个分区上。如果不了解您想要运行的查询类型,很难说分区是否是一个好主意。
我想我可以说将数据分成不同的表格是一个糟糕的主意,因为这是一个维护噩梦:
无论如何,开始的地方是Postgres关于分区的文档,here。我应该注意到Postgres的实现比其他数据库更加笨拙,因此您可能需要查看MySQL或SQL Server的文档以了解它正在做什么。
答案 1 :(得分:1)
首先,我想质疑你的问题的前提:
由于一个查询返回10 000多个项目,因此将这些数据存储在单个表格中显然不是一个好主意。
据我所知,没有一个根本原因可以解释为什么数据库无法处理数百万行的单个表。在极端情况下,如果您创建了一个没有索引的表,并且只是向其添加了行,那么Postgres可以直接将这些行写入磁盘,直到您的存储空间不足为止。 (内部可能有其他限制,我不确定;但如果是这样,他们大。)
只有在您尝试使用该数据做某事时才会出现问题,而确切的问题 - 以及确切的解决方案 - 取决于你做了什么。
如果要定期删除插入超过固定时间刻度的所有行,可以对createdAt
列上的数据进行分区。然后,DELETE
将成为非常高效的DROP TABLE
,并且所有INSERT
将通过触发器路由到“当前”分区(或者甚至可以通过导入脚本绕过它了解分区命名方案)。但是,SELECT
可能无法在其createAt
子句中指定一系列WHERE
值,因此需要查询所有分区并合并结果。你一次保留的分区越多,效率就越低。
或者,您可以检查表上的工作负载,并查看所有查询已经或可以轻松地显式声明rangeStart
值。在这种情况下,您可以在rangeStart
上进行分区,并且在规划每个SELECT
查询时,查询规划器将能够消除除一个或几个分区之外的所有分区。需要将INSERT
通过触发器路由到适当的表,并且维护操作(例如删除不再需要的旧数据)的效率会低得多。
或许您知道,一旦rangeEnd
变得“太旧”,您将不再需要这些数据,并且可以获得两个好处:rangeEnd
分区,确保所有SELECT
个查询明确提及rangeEnd
,并删除包含您不再感兴趣的数据的分区。
为了从git借用Linus Torvald的术语,用于分区的“管道”以表继承as documented here的形式构建到Postgres中,但除了示例之外,“瓷器”几乎没有。手册。但是,有一个非常好的extension called pg_partman,它提供了基于ID或日期范围管理分区集的功能;通过文档阅读以了解不同的操作模式非常值得。在我的情况下,没有一个完全匹配,但分支扩展比从头开始编写所有内容要容易得多。
请记住,分区不是免费的,如果根据上面的考虑因素没有明显的候选列进行分区,实际上最好将数据保留在一个表中,并考虑其他优化策略。例如,部分索引(CREATE INDEX ... WHERE
)可能能够处理最常查询的行子集;也许与“覆盖索引”相结合,Postgres可以直接从索引返回查询结果而不参考主表结构(“仅索引扫描”)。