要分区还是不分区?


8

已经阅读了关于SO,外部博客文章和手册的几个问题

我仍然发现自己想知道是否应该考虑我的情况进行分区。

案例-简化

存储客户数据。为了清楚起见,下面提到的所有表名均已组成。

  1. 具有需要客户识别的非物理对象,以及需要将某些对象按需发送回客户或以其他方式进行处理的情况下,实际存储它们的物理对象。它们以多对多关系映射。objects_nonphysicalobjects_physicalobjects_mapping_table

  2. 第二个多对多关系是这些非物理对象与其度量之间的关系。有些对象与某些指标绑定。metricsmetrics_objects_nonphysical

  3. 非物理对象和物理对象都有其子级关系表。objects_nonphysical_hierarchyobjects_physical_hierarchy

根据每个客户的需求和要求,可以提供有关物理对象的数据,或者可能需要从头开始创建。基本上,我需要做的是:

  • 保持内部系统的快速运行INSERTSELECT声明,因为这是要进行映射的地方。

  • 维护系统以供外部客户查看和操作其非物理对象 -快速检索数据。报表高效性的需求SELECT -许多客户可以随时使用此数据进行搜索。

我的考虑

可以有一个客户,他们可以访问数据,查看数据并对其进行操作,但是不必是我们从中获取数据/正在处理数据的承包商。

考虑到我总是知道应该将哪些分区数据归入(针对承包商的分区),然后考虑到需要为客户进行分区的外部客户的维护系统,这导致我将表分区引入到我的系统中(某些情况下可以做到这一点)延迟使用自动化工具和一组规则以客户的方式重写数据,因此对于每个客户,我们只为每个表扫描一个分区。

数据量

我的数据将不断增长,尤其是在导入新客户的对象和指标时。从长远来看,目前无法预测新数据进入系统的速度。确实,没有知道谁将成为下一个客户,就无法衡量它。眼下正好有2客户提供更多或更少的1M行对每个表的每个客户。但是将来我预计新客户也将有1000万行左右。

问题

这些问题都是相互关联的。

  1. 应该在这里真正考虑分区,还是过大?我认为它很有用,因为我始终只扫描一个分区。
  2. 如果要进行分区,那么如何FK考虑到我的需求最有效地实施约束?我应该选择constraint triggers还是将其保留在内部系统的应用程序层中,或者使用其他方法?
  3. 如果无法进行分区,那我应该深入研究什么呢?

如果没有足够的数据,请在下面的评论中让我知道。



3
通常,建议在没有索引,分区等任何开销的情况下开始生产。然后如有必要,添加索引和分区等
。– alonk

1
使用分区,您只会加快某些种类的查询的速度,而打击其他种类的查询。您还将对写入产生很大的影响。分区不应该是您要做的第一件事,并且我认为在可预见的将来使用普通索引会很好,并且在到达这些桥梁时就可以跨过它们。500万行不是那么大。这可能是一个有用的博客,可以进行速度比较:if-not-true-then-false.com/2009/…–
dizzystar

2
我同意头昏眼花的明星,我现在不会打扰。如果到达那座桥。当前在Postgres中进行分区还使得(如果不是不可能的话)很难使用正确的外键(这在9.7中可能会更改,但尚未解决)。即使具有50M行的表也不一定是分区的候选对象。如果你主要有在查询中减少的行数大致相等的条件下,良好的索引可以帮你长的路。
a_horse_with_no_name

1
对我而言,您不清楚“为承包商划分”的含义。承包商使用的表是否不同于属于客户的表?客户A是否必须访问客户B的数据?如果不是这样,那么将客户特定的数据分为每个客户的模式可能是一种方法-但这不一定是为了提高性能,而是为了关注点的分离(提高安全性/隐私性等)。
dezso

Answers:


1

您的问题有很多悬而未决的地方,但是按客户划分可能是最好的选择-特别是在以下情况下:

  • 您希望有很多客户,
  • 他们每个人可能都有大量的数据(“吨”意味着远远超过RAM缓存大小),
  • 他们的大多数数据集都是互斥的(每个客户看到不同的数据子集)。

规则或触发器是性能开销,可以避免。

考虑以下方面:

BEGIN;

CREATE USER tenant1;
CREATE USER tenant2;

CREATE SCHEMA app;
CREATE SCHEMA tenant1;
CREATE SCHEMA tenant2;

CREATE TABLE app.objects_nonphysical(id int);
CREATE TABLE app.objects_physical(id int);
CREATE TABLE app.objects_mapping(id int);    
CREATE TABLE tenant1.objects_nonphysical() INHERITS(app.objects_nonphysical);
CREATE TABLE tenant1.objects_physical() INHERITS(app.objects_physical);
CREATE TABLE tenant1.objects_mapping() INHERITS(app.objects_mapping);
CREATE TABLE tenant2.objects_nonphysical() INHERITS(app.objects_nonphysical);
CREATE TABLE tenant2.objects_physical() INHERITS(app.objects_physical);
CREATE TABLE tenant2.objects_mapping() INHERITS(app.objects_mapping);

GRANT USAGE ON SCHEMA tenant1 TO tenant1;
GRANT USAGE ON SCHEMA tenant2 TO tenant2;
GRANT SELECT,INSERT,UPDATE,DELETE ON ALL TABLES IN SCHEMA tenant1 TO tenant1;
GRANT SELECT,INSERT,UPDATE,DELETE ON ALL TABLES IN SCHEMA tenant2 TO tenant2;

/* TEST: simulate login as customer */
SET SESSION AUTHORIZATION tenant2;
/* No schema needed - default search_path works */
SELECT count(*) FROM objects_nonphysical; 

ROLLBACK;

您不需要任何触发器/规则来维护它。

这里有开放性的内容-仅仅是草案而已...一些问题:

  • 不“继承” PK,FK和索引。
  • 即使创建它们,也不会在主表上强制执行PK
  • 您可以通过对所有租户使用相同的顺序来克服此问题
  • 显然,必须针对此模型调整应用程序

0

如果您现在实现分区,那不会有什么坏处,但是使用单个分区,直到您的系统真正需要一个新分区为止。在性能方面,处理主键之类的开销很小。

我建议使用规则来重定向插入,并使用主表的外部表(例如CREATE TABLE objects_physical_ids (id bigserial NOT NULL PRIMARY KEY),连同在ids表中插入一行并将其复制到NEW.id的函数触发器(例如INSERT INTO objects_physical_ids DEFAULT VALUES RETURNING id INTO NEW.id;)以及其他处理删除的触发器和更新,以及为每个继承的表执行这些函数触发器的触发器(创建新的继承的表时不要忘记这样做!)然后,所有相关的表都可以FOREIGN KEY对相关的id表使用a(包括任何外键操作,例如ON UPDATEON DELETE)。


2
规则和触发器肯定有开销,并且很容易衡量。另外,它们增加了复杂性并使调试(非常)困难。另外,在以这种方式下降几次之后,我建议(不要无视所有细节)保留一个空的父表和一个或多个子分区。更改分区方案时,这可能非常方便。
dezso
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.