1

我正在编写一个在 Postgres DB 中使用分区的应用程序。这将发送给客户并在他们的服务器上运行。这意味着我必须为许多不同的场景做好准备。让我们从简单的表模式开始:

CREATE TABLE dir (
   id SERIAL,
   volume_id BIGINT,
   path TEXT
);

我想按 volume_id 列对该表进行分区。

我想达到的目标:

  • 分区数量有限(现在是 500,但我稍后会调整这个参数)
  • 不要一次创建所有分区 - 仅在需要时添加它们
  • 支持最大 100K 的卷 ID
  • [很高兴] - 人类能够根据 volume_id 计算分区号

我现在的解决方案:

  • 按 LIST 分区
  • 每个分区的处理volume_id % 500方式如下: CREATE TABLE dir_part_1 PARTITION OF dir FOR VALUES IN (1, 501, 1001, 1501, ..., 9501);

这很好用,因为我可以在需要时创建分区,并且我确切地知道给定的 volume_id 属于哪个分区。但是我必须手动声明数字并且我不能支持高volume_ids,因为插入语句的速度急剧下降(超过2倍)。

看起来我可以尝试 HASH 分区,但我最大的担心是我必须在一开始就创建所有分区,并且我希望能够在需要时动态创建它们,因为计划时间显着增加高达 5 秒500 个分区。例如,我知道我将添加带有volume_id=5. 如何判断应该创建哪个分区?

4

1 回答 1

1

我能够通过为分区表添加哈希运算符来强制 Postgres 使用虚拟哈希函数。

CREATE OR REPLACE FUNCTION partition_custom_bigint_hash(value BIGINT, seed BIGINT)
RETURNS BIGINT AS $$
    -- this number is UINT64CONST(0x49a0f4dd15e5a8e3) from
    -- https://github.com/postgres/postgres/blob/REL_13_STABLE/src/include/common/hashfn.h#L83
    SELECT value - 5305509591434766563;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;


CREATE OPERATOR CLASS partition_custom_bigint_hash_op
    FOR TYPE int8
    USING hash AS
    OPERATOR 1 =,
    FUNCTION 2 partition_custom_bigint_hash(BIGINT, BIGINT);

现在您可以像这样声明分区表:

CREATE TABLE some_table (
   id SERIAL,
   partition_id BIGINT,
   value TEXT
) PARTITION BY HASH (partition_id);

CREATE TABLE  some_table_part_2 PARTITION OF some_table FOR VALUES WITH (modulus 3, remainder 2);

现在您可以放心地假设允许带有的partition_id % 3 = 2行将落在some_table_part_2分区中。因此,如果您确定将在partition_id列中收到什么值,您可以只创建所需的分区。

免责声明 1:不幸的是,由于错误 #16840,这现在无法正常工作(Postgres 13.1)

免责声明 2:除非您计划创建大量分区(我会说 50 或更多)并且延长计划时间是一个问题,否则使用此技术没有意义。

于 2021-01-28T16:31:20.310 回答