13

我有N台客户端机器。我想用 BRIN 索引的不同分区加载每台机器。

这需要:

  • 使用预定义的分区数创建 BRIN - 等于客户端计算机的数量
  • 从在 BRIN 分区标识符上使用WHERE而不是在索引列上过滤的客户端发送查询

主要目标是在将单个表从 postgres 加载到分布式客户端机器时提高性能,保持客户端之间的行数相等 - 如果行数不除以机器数,则接近相等。

我目前可以通过维护新列来实现它,该列将我的表分成等于客户端机器数量(或row_number() over (order by datetime) % N即时使用)的桶数。这种方式在时间和内存方面效率不高,而 BRIN 索引看起来是一个不错的功能,可以加快此类用例的速度。

3 台客户端机器的最小可重现示例:

CREATE TABLE bigtable (datetime TIMESTAMPTZ, value TEXT);
INSERT INTO bigtable VALUES ('2015-12-01 00:00:00+00'::TIMESTAMPTZ, 'txt1');
INSERT INTO bigtable VALUES ('2015-12-01 05:00:00+00'::TIMESTAMPTZ, 'txt2');
INSERT INTO bigtable VALUES ('2015-12-02 02:00:00+00'::TIMESTAMPTZ, 'txt3');
INSERT INTO bigtable VALUES ('2015-12-02 03:00:00+00'::TIMESTAMPTZ, 'txt4');
INSERT INTO bigtable VALUES ('2015-12-02 05:00:00+00'::TIMESTAMPTZ, 'txt5');
INSERT INTO bigtable VALUES ('2015-12-02 16:00:00+00'::TIMESTAMPTZ, 'txt6');
INSERT INTO bigtable VALUES ('2015-12-02 23:00:00+00'::TIMESTAMPTZ, 'txt7');

预期输出:

  • 客户 1

2015-12-01 00:00:00+00, 'txt1'
2015-12-01 05:00:00+00, 'txt2'
2015-12-02 02:00:00+00, 'txt3'
  • 客户 2

2015-12-02 03:00:00+00, 'txt4'
2015-12-02 05:00:00+00, 'txt5'
  • 客户 3

2015-12-02 16:00:00+00, 'txt6'
2015-12-02 23:00:00+00, 'txt7'

问题:
如何使用预定义的分区数创建 BRIN 并运行过滤分区标识符而不是过滤索引列的查询?
可选地,BRIN(或其他 pg 好东西)可以加快从单个表并行加载多个客户端的任务的任何其他方式?

4

3 回答 3

1

听起来您想在多台机器上对表进行分片,并让每个本地表(全局表的一个分片)都有一个 BRIN 索引,其中只有一个存储桶。但这没有任何意义。如果单个 BRIN 索引范围覆盖整个(本地)表,那么它永远不会很有帮助。

听起来您正在寻找的是使用可用于分区排除的 CHECK 约束进行分区。PostgreSQL 长期以来一直支持表继承(尽管不是每个分区都在单独的机器上)。使用此方法,必须为每个分区显式设置 CHECK 约束所涵盖的范围。这种明确指定边界的能力听起来就像您正在寻找的那样,只是使用了不同的技术。

但是,分区排除约束代码不适用于模数。代码足够聪明,知道WHERE id=5只需要检查CHECK (id BETWEEN 1 and 10)分区,因为它知道 id=5 意味着 id 在 1 到 10 之间。更准确地说,它知道它的反义词。

但是,即使人类知道这一点,也从未编写过代码来知道这WHERE id=5意味着。id%10 = 5%10因此,如果您在模运算符上构建分区,而不是在范围上,如果您希望它利用约束,则必须CHECK (id%10=5)使用所有查询。WHERE id = $1 and id % 10= $1 %10

于 2016-01-30T19:13:39.237 回答
0

根据您的描述和评论,我会说您正在寻找错误的方向。您想预先拆分表,以便访问快速而简单,但不必预先拆分,因为这需要您预先知道节点的数量,如果我理解正确的话,这是一种变量。无论如何,拆分事物也需要相当多的处理。

老实说,我会以不同的方式解决您的问题。我宁愿建议在给定范围内为每条记录分配一个伪随机值,而不是将每条记录分配给存储桶。我不知道 Postgres 但在 MSSQL 中我会使用BINARY_CHECKSUM(NewID())而不是Rand(). 主要原因是随机函数在那里更难使用基于 SET 的函数。相反,您也可以使用一些返回合理工作空间的哈希码。无论如何,在我的 MSSQL 情况下,结果值将是一个有符号整数,位于 -2^31 到 +2^31 范围内的某个位置(给或取,检查文档以了解确切的边界!)。因此,当主机决定分配 n 台客户端机器时,可以为每台机器分配一个精确的范围——考虑到随机化器/散列算法的属性——将包含一个相当接近的工作负载除以 n 的近似值。假设您在选择字段上有一个索引,这应该相当快,无论您决定将表拆分为一千块还是一百万块。

PS:请注意,如果要处理的行数(大大)超过将进行处理的机器数量,这种方法只会“正常”工作。如果数量较少,您可能会看到几台机器没有得到任何东西,而其他机器则可以完成所有工作。

于 2016-01-31T15:53:16.973 回答
-1

基本上,您只需要知道加载后关系的大小,然后pages_per_range应该将存储参数设置为为您提供所需分区数量的除数。

无需引入人工的分区 ID,因为已经支持足够的类型和运算符。物理表布局在这里重要,因此如果您坚持分区 ID 作为键,并最终在自然加载顺序和人工分区 ID 之间引入乱序映射,请确保将表聚集在该列的创建 BRIN 之前的排序顺序。

但是,同时,请记住,更多的离散值比更少的值更有可能命中索引,因此高基数更好 - 人工分区标识符将具有自然键的基数的 1/n,其中 n 是每个分区的不同值。

更多herehere

于 2016-01-26T21:50:58.960 回答