本文描述问题及解决方法同样适用于 腾讯云 云数据仓库。
Greenplum(以下简称GP)有2种存储格式,Heap表和AO表(AORO表,AOCO表)。
Heap表是从PostgreSQL继承而来,使用MVCC来实现一致性。如果你在创建表的时候没有指定任何存储格式,那么GP就会使用Heap表。
Heap表支持分区表,只支持行存,不支持列存和压缩。需要注意的是在处理update和delete的时候,Heap表并没有真正删除数据,而只是依靠version信息屏蔽老的数据,因此如果你的表有大量的update或者delete,表占用的物理空间会不断增大,这个时候需要依靠vacuum来清理老数据。
Heap表不支持逻辑增量备份,因此如果要对Heap表做快照,每次都需要导出全量数据。
建表语句
CREATE TABLE heap(
a int,
b varchar(32)
) DISTRIBUTED BY (a);
最佳实践:
AO表是GP特有的,设计的目的就是为了数仓中大型的事实表。AO表支持行存和列存,并且也支持对数据进行压缩。
AO表无论是在表的逻辑结构还是物理结构上,都与Heap表有很大的不同。比如上文所述Heap表使用MVCC控制update和delete之后数据的可见性,而AO表则使用一个附加的bitmap表来实现,这个表的的内容就是表示AO表中哪些数据是可见的。
对于有大量update和delete的AO表,同样需要vacuum进行维护,不过在AO表中,vacuum需要对bitmap进行重置并压缩物理文件,因此通常比Heap的vacuum要慢。
AORO就是行存的AO表,同时行存也是AO表的默认存储方式。
AORO支持表级别的压缩,不支持列级别的压缩。
建表语句如下,重点是with后的appendonly=true,由于AO表默认是行存,因此orientation=row也可以不要,后面的compresstype=zlib, compresslevel=4都是压缩相关选项。
CREATE TABLE aoro(
a int,
b int,
c varchar(32),
d varchar(32)
)
WITH (appendonly=true, orientation=row, compresstype=zlib, compresslevel=4)
DISTRIBUTED BY (a)
压缩选项:
最佳实践____________________:
AOCO表就是列存的AO表。
AOCO不仅支持表级别的压缩,同时也支持列级别的压缩。
建表语句如下,这里还加入了分区特性,关于分区可以参见Greenplum性能优化之路 --(一)分区表:
CREATE TABLE aoco(
a int ENCODING (compresstype=zlib, compresslevel=5),
b int ENCODING (compresstype=none),
c varchar(32) ENCODING (compresstype=RLE_TYPE, blocksize=32768),
d varchar(32),
fdate date
)
WITH (appendonly=true, orientation=column, compresstype=zlib, compresslevel=6, blocksize=65536)
DISTRIBUTED BY (a)
PARTITION BY RANGE(fdate)
(
PARTITION pn START ('2018-11-01'::date) END ('2018-11-10'::date) EVERY ('1 day'::interval),
DEFAULT PARTITION pdefault
);
压缩选项:
BLOCKSIZE:
物理文件:
最佳实践:
单独列出这一节是因为Heap,AORO和AOCO这3种表在修改表结构时表现是不一样的,这也是大家容易忽视的地方。
对于不同的表类型,同样的修改语法耗时可能会差异很多,主要原因在于对于有些修改操作会导致表重写,而表重写的时间就取决于表本身的数据量。
以下列出了不同的表结构,在不同的ALTER语法下的行为,其中YES代表需要重写表,NO代表不需要重写表。
操作 | Heap | AORO | AOCO |
---|---|---|---|
ADD COLUMN | NO | YES | NO |
DROP COLUMN | NO | NO | NO |
ALTER COLUMN TYPE | YES | YES | YES |
ADD COLUMN DEFAULT NULL | YES | YES | NO |
ADD COLUMN DEFAULT VALUE | YES | YES | NO |
可以看出AOCO表由于每个列都是单独一个文件,因此在修改列结构时影响最小,这也是AOCO表的一个优势。
一张表是否可以同时使用多种存储方式呢?对于分区表,是可以的。
混合存储一般用于这样的场景,对于一张按时间分区的表,通常对于不同时间点的数据行为是不一样的,比如对于最近的数据,会有较多的明细查询,而对于比较老的数据,则是以分析为主。同时由于业务可能要保存较长时间的数据,为了节约成本,较老的数据会考虑使用压缩比较大的存储方式。
混合存储的关键就是使用到了GP的交换分区语法,也就是将一张独立的表与自己的一个分区表进行交换,当然这里前提是新表的结构是一样,并且交换的过程没有新数据进入。
流程如下:
1.创建一张分区表(1到5月份,每月一张表),采用Heap存储
CREATE TABLE hyper_storage (
a int,
b varchar(32),
fdate date
) DISTRIBUTED BY (a)
PARTITION BY RANGE(fdate)
(
PARTITION pn START ('2018-01-01'::date) END ('2018-06-01'::date) EVERY ('1 month'::interval),
DEFAULT PARTITION pdefault
);
storage=# \d
List of relations
Schema | Name | Type | Owner | Storage
--------+------------------------------+-------+--------------+---------
public | hyper_storage | table | test | heap
public | hyper_storage_1_prt_pdefault | table | test | heap
public | hyper_storage_1_prt_pn_1 | table | test | heap
public | hyper_storage_1_prt_pn_2 | table | test | heap
public | hyper_storage_1_prt_pn_3 | table | test | heap
public | hyper_storage_1_prt_pn_4 | table | test | heap
public | hyper_storage_1_prt_pn_5 | table | test | heap
2.现在要对1月份的表修改存储格式,因此创建一张新的AOCO表
CREATE TABLE exchange_table(
a int,
b varchar(32),
fdate date
) WITH (appendonly=true, ORIENTATION=column, compresstype=zlib, compresslevel=6)
DISTRIBUTED BY (a)
3.将1月份的数据导入新表
INSERT INTO exchange_table SELECT * FROM hyper_storage_1_prt_pn_1;
4.交换分区
ALTER TABLE hyper_storage EXCHANGE PARTITION pn_1 WITH TABLE exchange_table;
注:pn_1是1月份的分区表的partitionname,可以从pg_partitions中查询得到
5.查看结果
storage=# \d
List of relations
Schema | Name | Type | Owner | Storage
--------+------------------------------+-------+--------------+----------------------
public | hyper_storage | table | test | heap
public | hyper_storage_1_prt_pdefault | table | test | heap
public | hyper_storage_1_prt_pn_1 | table | test | append only columnar
public | hyper_storage_1_prt_pn_2 | table | test | heap
public | hyper_storage_1_prt_pn_3 | table | test | heap
public | hyper_storage_1_prt_pn_4 | table | test | heap
public | hyper_storage_1_prt_pn_5 | table | test | heap
public | exchange_table | table | test | heap
这样1月份的分区表就变成了AOCO表,而其他分区表仍然是Heap表
选取Heap,AORO,AOCO三种表,分别采用压缩和不压缩2种方式(Heap表不支持压缩,AO表压缩采用zlib格式,压缩级别设置为6),插入5亿条随机数据,然后使用
select pg_size_pretty(pg_relation_size('{tablename}'));
查看表所占大小,结果如下:
说明:可以看出Heap表占用空间更大,即使AO表不采用压缩。AOCO表由于是按列进行存储,所以相比行存的AORO表压缩比更大。当然这三者的差距取决于数据的实际情况,一般生产环境中Heap表不会和AO表有如此大的差距。
使用AOCO表,zlib压缩格式,选取不同的压缩级别,比较数据写入时间和表所占大小,由于zlib支持9个级别,这里选取1,6,9 三个级别进行比较,体现出趋势即可,结果如下:
说明:实际生产环境中不同压缩级别的数据,压缩比的差距可能会更大。但可以看出,越高的压缩级别,在插入的时候越耗时,其它SQL,类似SELECT,UPDATE等也都是一样。
切记,从其它系统迁移数据到GP上来,第一件事情就是给每张表选择合适的存储格式,特别是核心表。
欢迎阅读GP性能优化系列,下一篇Greenplum性能优化之路 --(三)ANALYZE
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。