通过块范围索引(BRIN)标识符直接查询Postgres表

时间:2016-01-16 11:24:38

标签: sql postgresql postgresql-9.5

我有 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'
  • client 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好东西)可以加速从单个表并行加载多个客户端的任务吗?

3 个答案:

答案 0 :(得分: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填充所有查询。

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:-1)

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

无需引入人工分区ID,因为支持足够的类型和运算符。物理表布局在此处非常重要,因此如果您坚持将分区ID作为密钥,并最终在自然加载顺序和人工分区ID之间引入无序映射,请确保在创建BRIN之前,将表格放在该列的排序顺序上。

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

更多herehere