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

Sql 直接按块范围索引(BRIN)标识符查询Postgres表,sql,postgresql,postgresql-9.5,Sql,Postgresql,Postgresql 9.5,我有N台客户机。我想用不同的BRIN索引分区加载每台机器 这需要: 创建具有预定义分区数的BRIN-等于客户端计算机数 从使用BRIN分区上的WHERE标识符而不是索引列上的筛选器的客户端发送查询 主要目标是在将单个表从postgres加载到分布式客户机时提高性能,保持客户机之间的行数相等,或者如果行数不除以机器数,则接近相等 目前,我可以通过维护新的列来实现这一点,该列将我的表分为与客户端计算机数量相等的存储桶数(或者动态使用row_number()(按日期排序)%N)。这样的话,它在时间

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

这需要:

  • 创建具有预定义分区数的BRIN-等于客户端计算机数
  • 从使用BRIN分区上的WHERE标识符而不是索引列上的筛选器的客户端发送查询
主要目标是在将单个表从postgres加载到分布式客户机时提高性能,保持客户机之间的行数相等,或者如果行数不除以机器数,则接近相等

目前,我可以通过维护新的列来实现这一点,该列将我的表分为与客户端计算机数量相等的存储桶数(或者动态使用
row_number()(按日期排序)%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

  • 客户2

  • 客户3

问题是:
如何使用预定义的分区数创建BRIN,并运行根据分区标识符过滤而不是索引列过滤的查询?

BRIN(或其他pg goodies)可以加速从单个表并行加载多个客户端的任务的任何其他方式?

基本上,您只需要知道加载后关系的大小,然后,
pages\u per\u range
storage参数应设置为提供所需分区数的除数

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

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


更多和。

听起来像是要在多台机器上切分一个表,并让每个本地表(全局表的一个切分)都有一个带有一个bucket的BRIN索引。但这毫无意义。如果单个BRIN索引范围覆盖了整个(本地)表,那么它永远不会有很大帮助

听起来您需要的是带有检查约束的分区,可以用于分区排除。PostgreSQL在很长一段时间内通过表继承支持这一点(尽管不是针对单独机器上的每个分区)。使用此方法,必须为每个分区显式设置检查约束中包含的范围。这种显式指定边界的功能听起来就像您正在寻找的一样,只是使用了一种不同的技术

但是,分区排除约束代码不能很好地使用模数。代码足够聪明,知道
其中id=5
只需要检查
检查(id介于1和10之间)
分区,因为它知道id=5意味着id介于1和10之间。更准确地说,它知道这正相反


但是代码从来没有被编写成知道
其中id=5
意味着
id%10=5%10
,即使人类知道这一点。因此,如果您在模数运算符上构建分区,比如
CHECK(id%10=5)
而不是在范围上,如果你想利用这些限制,你必须在所有的查询中加入
,其中id=$1和id%10=$1%10

根据你的描述和评论,我认为你看错了方向。您希望提前拆分表,这样访问将快速简单,但无需提前拆分,因为这需要您提前知道节点数,如果我理解正确,这是一种变量。不管怎样,分割东西也需要相当多的处理

老实说,我会以不同的方式处理你的问题。与其将每条记录分配给一个bucket,我更愿意建议在给定范围内为每条记录分配一个伪随机值。我不知道Postgres,但在MSSQL中,我会使用
二进制校验和(NewID())
而不是
Rand()
。主要原因是随机函数更难使用基于集合的方法。相反,您也可以使用一些哈希代码来返回合理的工作空间。无论如何,在我的MSSQL情况下,结果值将是一个介于-2^31到+2^31之间的有符号整数(给定或获取,请查看文档中的确切边界!)。因此,当主计算机决定分配n台客户机时,可以为每台计算机分配一个精确的范围,给定随机化器/散列算法的属性,该范围将相当接近工作量除以n的近似值。 假设您在选择字段上有一个索引,那么无论您决定将表拆分为1000块还是100万块,这都应该相当快


PS:请注意,只有当要处理的行数(大大)超过要处理的机器数时,这种方法才能“正常”工作。对于较小的数量,您可能会看到一些机器什么也得不到,而其他机器可以完成所有工作。

这可能在多个客户端上更成功,意味着多个会话。我认为您不能(几乎)以并发安全的方式(使用任何索引)平均分割表。或者是桌子上的?顺便说一句,为什么不是经典?(一)
2015-12-01 00:00:00+00, 'txt1'
2015-12-01 05:00:00+00, 'txt2'
2015-12-02 02:00:00+00, 'txt3'
2015-12-02 03:00:00+00, 'txt4'
2015-12-02 05:00:00+00, 'txt5'
2015-12-02 16:00:00+00, 'txt6'
2015-12-02 23:00:00+00, 'txt7'