美文网首页PostgreSQL开源GIS+空间数据应用开源
PostgreSQL分区方案测试(数据插入与分区表数量关系)

PostgreSQL分区方案测试(数据插入与分区表数量关系)

作者: 遥想公瑾当年 | 来源:发表于2019-03-06 14:56 被阅读66次

    一 前言

    单表数据量太大时,数据库通过建立分区表的方式,从而达到对大表进行拆分,使每个分区的子表数据规模适中,方便数据管理与常规查询分析等,分区表是数据库比较常用的数据管理需求。
    当前PostgreSQL 11版本及其社区目前分区方案整理如下:

    • Pg11自带分区表,目前分区类型有list,range,hash三种类型。
    • 社区分区插件pg_pathman,目前分区类型支持range,hash类型。
    • citus为代表的分布式数据节点分区方案,目前支持hash(更常用更通用)与append类型(不是很了解,不怎么常用,使用需谨慎)。
      这些方案都能在不同场景下支持不同的分区方案,本文选择一个方向对这些分区方案进行测试,即:某个表的分区表数量逐渐增多与数据插入性能衰减程度关系

    二 测试过程

    2.1 基准测试

    对某个表不做任何分区,使用pgbench压测,测试插入tps。
    建立表:

    drop table if exists shiptrack cascade;
    create table shiptrack(
        ship_id int not null, --船舶id
        pos_time timestamp without time zone not null, --坐标时间
        lon numeric not null,  --经度
        lat numeric not null --纬度
    );
    create index shiptrack_id_idx on shiptrack using btree(ship_id);
    create index shiptrack_time_idx on shiptrack using btree(pos_time);
    

    测试脚本 test.sql:

    \set _ship_id random(1,700000)
    \set x random(-180,180)
    \set y random(-90,90)
    INSERT INTO shiptrack(ship_id,pos_time,lon ,lat) values(:_ship_id,clock_timestamp(),:x, :y);
    

    pg_bench压测:

    [postgres@~]$ pgbench -M prepared -n -r -P 1 -f test.sql -c 80 -j 80 -T 30 -U postgres mytest
    transaction type: test.sql
    scaling factor: 1
    query mode: prepared
    number of clients: 80
    number of threads: 80
    duration: 30 s
    number of transactions actually processed: 1102316
    latency average = 2.179 ms
    latency stddev = 2.590 ms
    tps = 36616.746279 (including connections establishing)
    tps = 36662.855209 (excluding connections establishing)
    

    tps:36662

    2.2 pg自带分区表

    2.2.1 list分区

    建立表:

    drop table if exists shiptrack cascade;
    create table shiptrack(
        ship_id int not null, --船舶id
        pos_time timestamp without time zone not null, --坐标时间
        lon numeric not null,  --经度
        lat numeric not null --纬度
    ) partition by list(ship_id );
    create index shiptrack_time_idx on shiptrack using btree(pos_time);
    

    创建分区表,分区数量4:

    do language plpgsql $$  
     DECLARE
        i int;
        sql text;
     BEGIN
        for i in 1..4 loop
            sql:=format('create table if not exists shiptrack_%s PARTITION OF shiptrack for values in (%s)',i,i);
            execute sql;
            raise notice '%',sql;
        end loop;
    end;  
    $$;
    

    测试脚本 test.sql:

    \set _ship_id random(1,4)
    \set x random(-180,180)
    \set y random(-90,90)
    INSERT INTO shiptrack(ship_id,pos_time,lon ,lat) values(:_ship_id,clock_timestamp(),:x, :y);
    

    list分区数量4,tps:34492
    同理:
    list分区数量40,tps:21473
    list分区数量400,tps: 3220

    2.2.2 hash分区

    建立表:

    drop table if exists shiptrack cascade;
    create table shiptrack(
        ship_id int not null, --船舶id
        pos_time timestamp without time zone not null, --坐标时间
        lon numeric not null,  --经度
        lat numeric not null --纬度
    )  partition by hash(ship_id);
    create index shiptrack_id_idx on shiptrack using btree(ship_id);
    create index shiptrack_time_idx on shiptrack using btree(pos_time);
    

    创建分区表,分区数量4:

    do language plpgsql $$  
     DECLARE
        i int;
        sql text;
            tableCount int;
     BEGIN
            tableCount :=4;
        for i in 1..tableCount loop
            sql:=format('create table if not exists shiptrack_%s PARTITION OF shiptrack for VALUES WITH (MODULUS %s, REMAINDER %s)',i,tableCount,i-1);
            execute sql;
            raise notice '%',sql;
        end loop;
    end;  
    $$;
    

    测试脚本 test.sql:

    \set _ship_id random(1,700000)
    \set x random(-180,180)
    \set y random(-90,90)
    INSERT INTO shiptrack(ship_id,pos_time,lon ,lat) values(:_ship_id,clock_timestamp(),:x, :y);
    

    hash分区数量4,tps:32442
    同理:
    hash分区数量40,tps:20611
    hash分区数量400,tps: 3167

    2.2 pg_pathman测试(仅hash分区测试)

    建立表:

    drop table if exists shiptrack cascade;
    create table shiptrack(
        ship_id int not null, --船舶id
        pos_time timestamp without time zone not null, --坐标时间
        lon numeric not null,  --经度
        lat numeric not null --纬度
    ) ;
    create index shiptrack_id_idx on shiptrack using btree(ship_id);
    create index shiptrack_time_idx on shiptrack using btree(pos_time);
    

    创建分区表,分区数量4:

    select create_hash_partitions('shiptrack'::regclass,'ship_id',4,false); 
    

    测试脚本 test.sql:

    \set _ship_id random(1,700000)
    \set x random(-180,180)
    \set y random(-90,90)
    INSERT INTO shiptrack(ship_id,pos_time,lon ,lat) values(:_ship_id,clock_timestamp(),:x, :y);
    

    pg_pathman分区数量4,tps:29948
    同理:
    pg_pathman分区数量40,tps:28267
    pg_pathman分区数量400,tps: 23283
    pg_pathman分区数量1000,tps: 17922

    2.3 citus分布式表分区

    2.3.1 citus--逐条插入

    注意:citus分布式,各个环境与单机环境配置有差异,不太好同上单独比较
    建立表:

    drop table if exists shiptrack cascade;
    create table shiptrack(
        ship_id int not null, --船舶id
        pos_time timestamp without time zone not null, --坐标时间
        lon numeric not null,  --经度
        lat numeric not null --纬度
    ) ;
    create index shiptrack_id_idx on shiptrack using btree(ship_id);
    create index shiptrack_time_idx on shiptrack using btree(pos_time);
    

    创建分区表,分区数量4:

    --设置分区数量
    set citus.shard_count=4;
    --设置副本数量
    set citus.shard_replication_factor=2;
    --对表进行分区
    select create_distributed_table('shiptrack','ship_id','hash');
    

    测试脚本

    \set _ship_id random(1,700000)
    \set x random(-180,180)
    \set y random(-90,90)
    INSERT INTO shiptrack(ship_id,pos_time,lon ,lat) values(:_ship_id,clock_timestamp(),:x, :y);
    

    citus分区数量4,tps:25342
    同理:
    citus分区数量40,tps:26112。
    citus分区数量400,tps: 26817。
    citus分区数量1000,tps: 26524。
    逐条sql插入性能比较稳定。

    2.3.2 citus--批量插入

    建表和分区同上,只需更新下test.sql脚本为批量操作,单批次数量1000:

    \set _ship_id random(1,700000)
    \set x random(-180,180)
    \set y random(-90,90)
    INSERT INTO shiptrack(ship_id,pos_time,lon ,lat) select 
    :_ship_id,clock_timestamp(),:x, :y from generate_series(1,1000);
    

    创建表:

    drop table if exists shiptrack cascade;
    create table shiptrack(
        ship_id int not null, --船舶id
        pos_time timestamp without time zone not null, --坐标时间
        lon numeric not null,  --经度
        lat numeric not null --纬度
    ) ;
    create index shiptrack_id_idx on shiptrack using btree(ship_id);
    create index shiptrack_time_idx on shiptrack using btree(pos_time);
    
    --设置分区数量
    set citus.shard_count=4;
    --执行分区
    select create_distributed_table('shiptrack','ship_id','hash');
    

    执行压测语句:

    [postgres@~]$ pgbench -M prepared -n -r -P 1 -f test.sql -c 80 -j 80 -T 30 -U postgres mytest
    

    通过设置set citus.shard_count为4,40,400,450,550,700,1000,重试上述步骤,得到tps依次是:
    citus分区数量4,tps:1563
    同理:
    citus分区数量40,tps:1549。
    citus分区数量400,tps: 1583。
    citus分区数量450,tps: 1115。
    citus分区数量550,tps: 1063。
    citus分区数量700,tps: 895。
    citus分区数量1000,tps: 706。

    三 测试现象与总结

    单表不分区:tps:36662
    数据插入与分区表数量关系测试汇总如下表:

    测试方式 分区数 4 分区数 40 分区数 400 分区数 1000
    pg自带分区 list 34492 21473 3220 未测试
    pg自带分区 hash 32442 20611 3167 未测试
    pg_pathman hash 29948 28267 23283 17922
    citus hash(单条插入) 25342 26112 26817 26524
    citus hash(批量插入) 1563 1549 1583 706

    现象:

    • 随着分区表数量增加,除了citus都有性能衰减现象。
    • pg_pathman衰减比较平滑,pg自带的分区表指数级别衰减。
    • pg自带的hash分区,性能稍微弱于自带的list分区。
    • citus单条插入性能与分区数量递增关系几乎没有影响。
    • citus以1000数据量为一批次,在分区数为400之前稳定,从400之后开始平滑衰减。

    个人总结:

    • pg11自带的分区,适用于分区数量不大的业务场景。
    • 在单机pg分区中,如果分区数量比较大,建议采用pg_pathman,可以获取更好的性能。
    • 在具备分布式数据节点的前提下,使用citus的分区在插入和查询都能有较好的表现。

    相关文章

      网友评论

        本文标题:PostgreSQL分区方案测试(数据插入与分区表数量关系)

        本文链接:https://www.haomeiwen.com/subject/vxdouqtx.html