2015-09-26 47 views
2

我的软件每30分钟运行一次cronjob,它会从Google Analytics/Social网络中提取数据并将结果插入Postgres数据库。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-20百万行,Postgres的应该仍然能够快速运行SELECT语句,对不对?插入不一定要超快。

+1

关于此主题的[官方文档](http://www.postgresql.org/docs/9.4/static/ddl-partitioning.html)精辟,应该足够。 – klin

+0

参见[pg_partman](http://pgxn.org/dist/pg_partman/doc/pg_partman.html),这是一个易于使用的扩展,提供了内置管道缺少的许多细节。 – IMSoP

回答

5

这太长了评论。

分区在各种情况下都是一个好主意。两个想到的是:

  • 您的查询有一个WHERE子句,可以很容易地映射到一个或几个分区。
  • 您想快速删除历史数据(删除分区比删除记录快)。

不知道要运行的查询类型,很难说分区是否是一个好主意。

我想我可以说,数据分割成不同的表是主意,因为它是一个维护的噩梦:

  • 你不能有外键引用到表中。
  • 跨越多个表的查询非常麻烦,所以很简单的问题很难回答。
  • 维护表成为一场噩梦(添加/删除列)。
  • 如果您拥有不同角色的用户,则必须谨慎维护权限。

无论如何,开始的地方是Postgres关于分区的文档,它是here。我应该注意到Postgres的实现比其他数据库更笨拙,因此您可能需要查看MySQL或SQL Server的文档以了解它正在做什么。

+0

感谢您的回复。我无法确切地告诉我们需要哪些查询,因为历史数据将由另一个应用查询。但我认为最重要的关键是日期范围(从 - 到),因此基于此创建分区也许是个好主意。 – user2297996

1

首先,我想挑战你的问题的前提是:

由于一个查询返回的10个000多个项目,这显然不是存储在一个表中这个数据是个好主意。

据我所知,没有根本的原因,为什么数据库不能很好地处理数百万行的单个表。在极端情况下,如果您创建了一个没有索引的表格,并且只需将行添加到它,Postgres就可以继续将这些行写入磁盘,直到您耗尽存储空间。 (可能还有其他的限制内,我不知道。但即便如此,他们)当您尝试做一些与数据

的问题只来了,确切的问题 - 因此确切的解决方案 - 取决于你做什么

如果要定期删除其是不是一个固定的时间表前插入更多的所有行,你可以在createdAt列分区的数据。然后DELETE将变成非常高效的DROP TABLE,并且所有INSERT将通过触发器路由到“当前”分区(或者如果您的导入脚本知道分区命名方案,甚至可以绕过它)。但是,SELECT s可能无法在其WHERE子句中指定范围createAt值,因此需要查询所有分区并合并结果。您一次保留的分区越多,效率就越低。

或者,您可以检查表上的工作负载,并查看所有查询已经或很容易明确指出rangeStart值。在这种情况下,您可以在rangeStart上进行分区,并且在规划每个SELECT查询时,查询计划程序将能够消除除一个或几个分区以外的所有分区。 INSERT需要通过触发器路由到适当的表,并且维护操作(例如删除不再需要的旧数据)效率会低得多。

或者,也许你知道,一旦rangeEnd变得“太旧”你将不再需要的数据,并能得到两个好处:通过rangeEnd分区,确保所有SELECT查询明确提到rangeEnd,并包含你的数据下降分区不再感兴趣

为了从git借用Linus Torvald的术语,分区的“管道”以表继承的形式构建到Postgres中,但是除了示例以外,“瓷器”的用法很少在手册中。但是,有一个很好的extension called pg_partman,它提供了基于ID或日期范围管理分区集的功能;阅读文档以了解不同的操作模式是非常值得的。在我的情况下,没有任何匹配,但是分叉​​比从零开始写所有事情要容易得多。

请记住,分区并不是免费的,并且如果根据上述考虑因素没有明显的列分区候选者,那么实际上最好将数据留在一个表中,并考虑其他优化策略。例如,部分索引(CREATE INDEX ... WHERE)可能能够处理最常查询的行子集;也许与“覆盖索引”相结合,其中Postgres可以直接从索引返回查询结果而不参考主表结构(“仅索引扫描”)。

+0

感谢您的详细解释,这非常有用!我已经检查过这些文档,现在会检查pg_partman。 到目前为止,created似乎是一个很好的专栏来分区。我研究了查询代码,最重要的查询可以明确提及它(作为范围)。 – user2297996

+0

关于将数据保存在一张表中:这是一个选项。但即使该表正确索引,如果有5亿行,那么SELECT也会相对较慢,除非索引以某种方式确保只在每个查询中检查一小部分数据集。因此,使用分区或其他技术来分割数据似乎是一种更好的方法。我错了吗? – user2297996

+0

@ user2297996“,除非索引以某种方式保证只有一小部分数据集必须在每个查询上进行检查” - 这与索引非常类似,可以高效地跳到数据集的右侧部分。我不知道* how * btree索引的工作细节,但根据我对数据库的经验法则,如果我不知道它是如何工作的,那么DBMS可能会做出比我更好的决策试图通过手工优化低级结构。 – IMSoP