使用PostgreSQL插件pg_pathman对超大表分表的实践
Posted 数据产品漫谈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用PostgreSQL插件pg_pathman对超大表分表的实践相关的知识,希望对你有一定的参考价值。
在时序相关的数据库中,当收集数据的粒度达到秒级时,数据库的膨胀是一个不可避免的问题。当数据量达到亿级甚至十亿级时,单表的查询性能和插入性能都会受到影响,因此要考虑对单个大表进行分表操作。
PostgreSQL传统的分区方法,使用约束来区分不同分区的数据,所以在查询操作时执行计划根据约束条件来选择分区表,在插入操作时使用触发器和规则来将数据插入相应的分区表。这种方式对性能有较大的影响。
首先要确保使用的PostgreSQL版本在9.5以上,因为pg_pathman只支持PostgreSQL9.5以上的版本,并且要确保已经安装了pg_stat_statements插件,这个插件是PG中监控数据库活动的重要插件,通过它可以获得SQL的统计信息,例如SQL被调用了多少次,返回了多少记录,在读写数据上花了多少时间,这个对于数据库的监控很有帮助。如果没有安装这个插件的,可以自行搜索相关安装办法。
在安装之前要添加两条环境变量:
[gpadmin@s01~]$ exportD_LIBRARY_PATH=/usr/local/pgsql/lib:$LD_LIBRARY_PATH
[gpadmin@s01~]$ export PATH=/usr/local/pgsql/bin:$PATH
如果机器上有多个pgsql版本的话,要添加想要使用的pgsql的相关路劲。
然后下载pg_pathman插件:
[gpadmin@s01~]$ git clone https://github.com/postgrespro/pg_pathman
[gpadmin@s01~]$ cd pg_pathman
[gpadmin@s01 pg_pathman]$ make USE_PGXS=1
[gpadmin@s01 pg_pathman]$ make USE_PGXS=1 install
修改PG的配置文件:
进入到PG的数据目录
[gpadmin@s01 pg_pathman]$ cd $PGDATA
[gpadmin@s01 data]$ vim postgresql.conf
将shared_preload_libraries的注释取消并修改该条目为
shared_preload_libraries = ‘pg_pathman, pg_stat_statements’
重新启动数据库
[gpadmin@s01 bin]$ ./pg_ctl restart -D$PGDATA
[gpadmin@s01 bin]$ ./psql -h 127.0.0.2 -p6543 -d data
psql (9.6.2)
Type "help" for help.
Data=# create extension pg_pathman;
CREATEEXTENSION
Data=# \dx
List of installed extensions
Name | Version | Schema | Description
------------+---------+------------+----------------------------------
pg_pathman | 1.4 | public | Partitioning tool for PostgreSQL
可以看到插件已经安装成功了。
表结构:
CREATE TABLE data
(
last double precision,
volume double precision,
ask10 double precision,
ask9 double precision,
ask8 double precision,
ask7 double precision,
ask6 double precision,
ask5double precision,
ask4 double precision,
ask3 double precision,
ask2 double precision,
ask1 double precision,
bid1 double precision,
bid2 double precision,
bid3 double precision,
bid4 double precision,
bid5 double precision,
bid6 double precision,
bid7 double precision,
bid8 double precision,
bid9 double precision,
bid10 double precision,
asize10 double precision,
asize9 double precision,
asize8 double precision,
asize7double precision,
asize6 double precision,
asize5 double precision,
asize4 double precision,
asize3 double precision,
asize2 double precision,
asize1 double precision,
bsize1 double precision,
bsize2 double precision,
bsize3 double precision,
bsize4 double precision,
bsize5 double precision,
bsize6 double precision,
bsize7 double precision,
bsize8 double precision,
bsize9 double precision,
bsize10 double precision,
windcode text NOT NULL,
datetime timestamp without time zone,
date text NOT NULL,
index integer NOT NULL,
CONSTRAINT "STOCK_DATA_PK" PRIMARY KEY (windcode, date, index)
);
主键为(windcode, date, index)复合主键
pg_pathman提供了range和hash两种分表方式,根据我们的使用场景,我们通过对股票代码进行hash分表。
分表函数指定主表OID,分区列名和分区个数:
create_hash_partitions(relation REGCLASS, -- 主表OID
attribute TEXT, --分区列名
partitions_count INTEGER, -- 打算创建多少个分区
partition_data BOOLEAN DEFAULT TRUE) --- 是否立即将数据从主表迁移到分区
不建议立即将数据从主表迁移到分区, 建议使用非堵塞式的迁移( 调用partition_table_concurrently())
注意分区列必须要有NOT NULL约束。
实例:
Data=# select create_hash_partitions('data'::regclass, -- 主表OID
'windcode', -- 分区列名
128, -- 打算创建多少个分区
false) ; -- 不迁移数据
create_hash_partitions
------------------------
128
(1 row)
Data=# \d+
可以查看到相关信息
postgres=# select count(*) from onlypart_test;
count
-------
10000
(1 row)
使用非堵塞式的迁移接口迁移数据
partition_table_concurrently(relation REGCLASS -- 主表OID
batch_size INTEGERDEFAULT 1000, -- 一个事务批量迁移多少记录
sleep_time FLOAT8DEFAULT 1.0) -- 获得行锁失败时,休眠多久再次获取,重试60次退出任务
实例:
Data=# selectpartition_table_concurrently('data'::regclass,
10000,
1.0);
NOTICE: worker started, you can stop itwith the following command: select stop_concurrent_part_task('part_test');
partition_table_concurrently
------------------------------
(1 row)
在分表过程中可以使用下面的指令来查看进度
Data=# select * frompathman_concurrent_part_tasks;
经过实际评测,1亿条数据的分表时间在2个小时左右,17亿条数据的分表时间在30个小时左右,根据机器性能有浮动。
分表后的性能测试:
主键为(windcode, date, index)的复合主键,构造的数据有3000个不同的windcode,每个windcode分别有38596个index,总计约1.2亿条数据。根据windcode(text类型)进行hash分表,共分为64个分区表。分区表的条数在150万条到200万条之间浮动。总体而言数据分配还是比较均匀的。
分区后对每个分区表进行count(*)操作,执行计划为Seq Scan,花费时间为5500ms左右。
分区后对windcode字段进行count(*)操作,例如:
select count(*) from stock_data2 wherewindcode=’001000.SH’;
执行计划为先对主表进行Seq Scan,然后对分区表进行Bitmap Heap Scan和Bitmap Index Scan,时间从5000到16000ms之间浮动,与当时机器的资源占用率有关。没有再出现过查询时间超过250000ms的情况。
分区后主表进行插入工作,插入的条目会自动插到各个分区表中,单个条目的插入时,执行计划为CustomScan (PartitionFilter),时间为0.2ms
构造函数对3000个windcode分别插入一条数据,插入时间为800到30000ms之间浮动,与当时机器的资源占用有关。
分区后对主表进行count(*)操作,执行计划为Append,然后对主表和各个分区表进行Seq Scan,时间为300,000到1000,000ms之间浮动。
跟单表进行比较,在插入和总表count方面没有提升,但是在对windcode字段进行count操作时,有5~10倍的性能提升。
当数据达到17亿级别时,笔者对用实际操作中的select命令进行了测试。
例如
select * from “WIND”.”STOCK_CN_4001”where windcode=’000955.SZ’ and date=’2017-12-01’ and datetime > ‘2017-12-01 09:30:00’and datetime < ‘2017-12-01 14:57:00’ order by datetime;
查询时间又从5000ms提升到5ms,性能有非常高的提升。
对windcode做select count(*)操作,实际执行时间为17200ms。
但是在实际使用中,发现pg_pathman分表以后分区表不支持upsert功能(insert on conflict do),这对经常需要进行该操作的业务场景来说很不方便。目前正在研究解决办法,PG10自带的分区表工具支持upsert功能,但不支持hash分表,只支持range分表和list分表。如果要在PG9中解决这个问题,应该需要到业务层代码中进行判断。好消息是PG11的分区功能有很大提升,拭目以待。
以上是关于使用PostgreSQL插件pg_pathman对超大表分表的实践的主要内容,如果未能解决你的问题,请参考以下文章
云小课|教你如何使用RDS for PostgreSQL插件
Nacos2.2使用PostgreSQL数据源插件存储数据手把手教程
Nacos2.2使用PostgreSQL数据源插件存储数据手把手教程
如何在VS2015中使用PostgreSQL插件静态编译Qt 5.8