在 Postgresql 中进行水平分区的正确步骤是啥?

Posted

技术标签:

【中文标题】在 Postgresql 中进行水平分区的正确步骤是啥?【英文标题】:What would be the right steps for horizontal partitioning in Postgresql?在 Postgresql 中进行水平分区的正确步骤是什么? 【发布时间】:2012-05-02 16:05:15 【问题描述】:

我们有一个带有 Postgresql 9.1 数据库的电子商务门户。一张非常重要的表目前有 3200 万条记录。如果我们要交付所有项目,该表将增长到 3.2 亿条记录,主要是日期。这会很重。

所以我们正在考虑水平分区/分片。我们可以将这张表中的项目水平分成 12 份(每月 1 份)。这样做的最佳步骤和技术是什么?数据库中的水平分区是否足够好,还是我们必须开始考虑分片?

【问题讨论】:

【参考方案1】:

如果您不介意升级到 PostgreSQL 9.4,那么您可以使用 pg_shard extension,它可以让您透明地将 PostgreSQL 表分片到多台机器上。每个分片都存储为另一个 PostgreSQL 服务器上的常规 PostgreSQL 表,并复制到其他服务器。它使用哈希分区来决定将哪些分片用于给定查询。如果您的查询具有自然分区维度(例如客户 ID),那么 pg_shard 会很好地工作。

更多信息:https://github.com/citusdata/pg_shard

【讨论】:

【参考方案2】:

这是我的分区示例代码: t_master 是要在您的应用程序中选择/插入/更新/删除的视图 t_1 和 t_2 是实际存储数据的基础表。

create or replace view t_master(id, col1)
as 
select id, col1 from t_1
union all
select id, col1 from t_2


CREATE TABLE t_1
(
  id bigint PRIMARY KEY,
  col1 text
);

CREATE TABLE t_2
(
  id bigint PRIMARY KEY,
  col1 text
);



CREATE OR REPLACE FUNCTION t_insert_partition_function()
returns TRIGGER AS $$
begin
raise notice '%s', 'hello';
    execute 'insert into t_'
        || ( mod(NEW.id, 2)+ 1 )
        || ' values ( $1, $2 )' USING NEW.id, NEW.col1 ;
    RETURN NULL;
end;
$$
LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION t_update_partition_function()
returns TRIGGER AS $$
begin
    raise notice '%s', 'hello';
    execute 'update t_'
        || ( mod(NEW.id, 2)+ 1 )
        || ' set id = $1, col1 = $2 where id = $1' 
        USING NEW.id, NEW.col1 ;
    RETURN NULL;
end;
$$
LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION t_delete_partition_function()
returns TRIGGER AS $$
begin
    raise notice '%s', 'hello';
    execute 'delete from t_'
        || ( mod(OLD.id, 2)+ 1 )
        || ' where id = $1' 
        USING OLD.id;
    RETURN NULL;
end;
$$
LANGUAGE plpgsql;



CREATE TRIGGER t_insert_partition_trigger instead of INSERT
ON t_master FOR each row 
execute procedure t_insert_partition_function();

CREATE TRIGGER t_update_partition_trigger instead of update
ON t_master FOR each row 
execute procedure t_update_partition_function();

CREATE TRIGGER t_delete_partition_trigger instead of delete
ON t_master FOR each row 
execute procedure t_delete_partition_function();

【讨论】:

【参考方案3】:

虽然 3.2 亿并不小,但也算不上很大。

这在很大程度上取决于您在表上运行的查询。如果您始终在查询中包含分区键,那么“常规”分区可能会起作用。

可以在 PostgreSQL wiki 中找到一个示例:http://wiki.postgresql.org/wiki/Month_based_partitioning

手册还解释了分区的一些注意事项:http://www.postgresql.org/docs/current/interactive/ddl-partitioning.html

如果您正在考虑分片,您可以阅读 Instagram(由 PostgreSQL 提供支持)是如何实现的:

http://instagram-engineering.tumblr.com/post/10853187575/sharding-ids-at-instagram

如果您主要有读取查询,另一种选择可能是使用流式复制来设置多个服务器并通过连接到热备用进行读取访问并连接到主服务器进行写入访问来分发读取查询。我认为pg-pool II 可以自动(在某种程度上)做到这一点。这可以与分区相结合,以进一步减少查询运行时间。

如果您喜欢冒险并且没有非常迫切的需要这样做,您也可以考虑 Postgres-XC,它承诺支持透明的水平缩放:http://postgres-xc.sourceforge.net/

目前还没有最终版本,但看起来不会花费太长时间

【讨论】:

非常感谢您的深刻见解! 作为一个数据点,我们的商店在我们访问量最大的一张表中拥有超过 3 亿行,没有分区或分片,而且运行良好。重申上述一些内容,使分区有价值的关键因素是拥有一个分区键,该分区键通常用于限制查询中感兴趣的行,并希望定期删除整个分区。 (删除分区比删除 1/12 行要快得多。)

以上是关于在 Postgresql 中进行水平分区的正确步骤是啥?的主要内容,如果未能解决你的问题,请参考以下文章

postgresql 查询中的分区给出不明确的结果

PostgreSQL分区表

如何使用 AWS Glue Crawler 读取 PostgreSQL 表分区?

PostgreSql Partition + Hibernate Insert

是否可以使用 JetBrains DataGrip 对表进行分区? (PostgreSQL)

关系数据库集群