文档首页/ 数据仓库服务 GaussDB(DWS)/ 最佳实践/ 存算分离/ GaussDB(DWS) 3.0 存算分离使用建议及性能优化
更新时间:2024-11-08 GMT+08:00

GaussDB(DWS) 3.0 存算分离使用建议及性能优化

场景介绍

GaussDB(DWS)全新推出云原生数仓DWS 3.0版本,利用云基础设施提供的资源池化和海量存储能力,结合MPP数据库技术,采用计算存储分离架构,实现了极致弹性、实时入库、数据实时共享和湖仓一体等特性。

了解更多存算分离知识,请参见什么是数据仓库服务

本文档主要描述存算分离版本特有的性能优化和注意事项。

集群购买

  • EVS磁盘空间

    9.1.0.x版本只是实现了将列存用户数据存储到OBS上,其它数据仍然保存在本地盘。因此,即使在存算分离架构下,也并不意味着不需要额外配置EVS磁盘或者只需要配置一个很小的磁盘,后续版本存算分离能力将会持续演进。

    表1 表类型的存储说明

    表类型

    存储位置

    适用场景

    行存表/临时表/列存索引

    本地,无压缩

    点查、实时小批量入库、频繁更新。

    列存表2.0

    本地,有压缩

    批量入库、查询、实时小批量入库、点查、更新。

    列存表3.0

    OBS,有压缩

    批量入库、查询、低频批量更新。

    EVS存储内容:行存、列存元数据(min/max)、 索引、Delta、WAL、OBS数据缓存、计算中的临时下盘文件(sort/hash),购买DWS集群时可指定大小。

    EVS存储总大小购买计算公式

    ( 2副本 *(行存表大小 + 索引大小 + Delta表大小) + OBS热数据缓存大小) / 0.8(预留)

    当EVS存储总大小超过90%时会触发集群只读,预留的10%空间保存WAL和临时文件下盘。

    • OBS热数据:1. 明确知道热数据的大小;2. 如果不知道,可以选择OBS总数据量 *30%。
    • 列存索引大小 = 原始未压缩数据size * 索引列宽 * 3(膨胀) / 总列宽
    • 列存数据按照3倍压缩比评估,假设20列的表,2列做主键,索引就是压缩前数据的30%,和压缩后数据相当。
    • Delta大小:一个表(或者一个分区) Max(10GB, 压缩后表大小 / 10)
    • 行存索引按照30%评估。

    EVS磁盘空间推荐:如果按以上算法估算,实际操作比较麻烦,建议:EVS磁盘空间总大小设置为压缩后的总数据量大小(压缩比一般按5倍计算),如果表上没有索引(EVS主要做缓存使用),EVS磁盘空间总大小可以设置为总数据量(可以排除掉归档数据的大小)大小的50%或者30%,并调大磁盘缓存的大小(见下文)。

    最小容量

    • 性能客户:保证每个DN主备各挂载的磁盘容量最少要500G(以达到每块盘350MB/s的带宽),比如一个ECS上部署了2主2备,该机器要至少挂载 4 * 500G 磁盘容量。
    • 成本敏感客户:每个DN主备各挂载最小200G(每块盘带宽160MB/s)。
  • OBS配置

    OBS要求3AZ部署并支持并行文件系统。

    OBS性能

    如需调整OBS性能指标,请联系技术支持。公有云场景,6个DN及以下节点集群,OBS指标一般不需要调整。

  • CPU配置

    建议生产环境每个节点16U起步,4U/8U仅用于体验。

    • 搬迁场景:与搬迁对象保持一致。
    • 新建场景:根据总数据量/100GB,计算CPU核数(与实际场景有关,计算密集型的应该增大CPU核数)。

表设计优化

建表语句

由于DWS默认建表类型为行存表,对于OLAP分析场景,建表需显式设置ORIENTATION 选项为列存。

更多内容请参见SQL语法的CREATE TABLE章节。

1
CREATE TABLE public.t1(id integer) WITH (ORIENTATION =COLUMN);

表设计优化

存算分离架构下,数据存储在OBS,使用过滤手段来避免不必要的远程CU数据的读取开销,对存储分离性能优化具有更大的价值。

过滤手段

GaussDB(DWS)兼容PostgreSQL生态,行存及其btree索引和PostgreSQL类似,列存及其索引为自研。建表时,选择合适的存储方式、分布列、分区键、索引,能够使SQL在执行时快速命中数据,减少IO消耗。下图是一个SQL从发起到获取数据的执行流程,通过下图,可以理解每个技术手段的作用,用于指导性能调优。

图1 SQL执行流程
  1. SQL执行时,分区表会通过Partition Column分区裁剪,定位到所在分区。
  2. Hash分布表会通过Distribute Column快速定位到数据所在的数据分片(存算一体架构下定位到DN,存算分离架构下定位到Bucket)。
  3. 行存通过btree快速定位到数据所在的Page;列存通过min-max索引快速定位到可能存在数据的CU数据块,PCK(Partial Cluster Key)列上的min-max索引过滤效果最好。
  4. 系统自动为列存的所有列维护min-max索引,无需用户定义。min-max索引是粗过滤,满足min-max条件的CU数据块不一定真正存在满足filter条件的数据行,如果定义了bitmap column,可以通过bitmap index快速定位到CU内符合条件的数据所在的行号;对于有序的CU,也会通过二分查找快速定位数据的行号。
  5. 列存也支持btree和gin索引,通过btree/gin索引也可以快速定位到满足条件的数据所在的CU及行号,但索引的维护代价比较大,除非对点查有极高的性能诉求,否则推荐使用bitmap index替代btree/gin。

优化手段

以一个建表语句为例,描述DWS已有的优化手段。更多内容请参见SQL语法的CREATE TABLE章节。

表2 优化手段

编号

优化手段

使用建议

SQL示例

建表后是否可修改

1

字符串类型

  • 字符串类型会比定长类型慢,能用定长类型的场景不建议使用字符串类型。
  • 能指定长度在16以内的尽量指定,性能会翻倍提高,如果不能指定长度16以内,该优化手段不受益。

-

是,已有数据会重写。

2

Numeric类型

Numeric类型要求都指定精度,性能会翻倍提高,尽量不要用无精度的Numeric。

--

是,已有数据会重写。

3

Partition by Column

  • 需用户定义,使用分区表,通过分区键进行剪枝,支持partition wise join,适用于等值和范围查询场景。
  • 分区数不宜超过1000,分区列不宜超过2列。
1
SELECT * FROM t1 WHERE t1.c1='p1';

否,如需修改请重新建表。

4

secondary_part_column

  • 需用户定义,只适用于列存表,适用于等值查询。
  • 可以在最常用的一个等值过滤上指定二级分区。
1
SELECT *  FROM t1 WHERE t1.c1='p1';

否,如需修改请重新建表。

5

Distribute by Column

需用户定义,适用于频繁进行group by或者多表join的join字段,通过local join减少数据shuffle,适用于等值查询。

1
SELECT * FROM t1 join t2 on t1.c3 = t2.c1;

否,如需修改请重新建表。

6

Bitmap_columns

需用户定义,根据CU内的重复值,自适应创建bitmap index(基数<=32),或者bloom filter(基数>32),适用于varchar/text类型列的等值查询场景,建议定义到where条件涉及的列。

1
SELECT * FROM t1 WHERE t1.c4 = 'hello';

可以修改,已有数据不重写,只影响新数据。

7

Min-max索引

  • min-max索引无需用户定义,适用于等值和范围查询场景。
  • min-max的过滤效果取决于数据的有序性,指定PCK的列,过滤效果会更好。
1
SELECT * FROM t1 WHERE c3 > 100 and c3  < 200

PCK可以修改,已有数据不重写,只影响新数据。

8

主键(btree索引)

  • upsert入库强依赖主键,需用户定义,适用于等值和范围查询场景。
  • 满足业务前提下,能使用定长类型的列更好,定义时把Distinct值多的列尽可能放到前面。
1
SELECT * FROM t1 WHERE c3 >100 and c3 < 200;

可以修改,重建索引。

Gin索引

  • 需用户定义,适用不固定的多条件等值查询,Distinct值超过100W的列不建议使用。
  • 适用于过滤后数据量小于1000的场景,过滤后数据量依然较大的,不推荐使用。
1
SELECT * FROM t1 WHERE c1 = 200 and c2 = 105;

可以修改,重建索引。

9

Orientation=column/row

指定表为行存或者列存,行存无压缩,适合点查和频繁更新场景;列存有压缩,适合分析场景。

-

否,如果修改请重新建表。

关于磁盘缓存

DWS会把经常访问的数据缓存到EVS本地磁盘,以减少OBS直读次数,加速查询性能。磁盘缓存只在DN计算节点上存在,协调节点CN上不存在。

缓存大小

集群默认的缓存大小(disk_cache_max_size)配置为:EVS容量的1/2。

EVS容量默认划分是:1/2存储本地持久化的数据(如:列存索引,行存表,本地列存表),另外1/2给缓存用。DWS的索引不同于Redshift,Redshift索引只是一个优化器提示,没有实体的索引数据,DWS的索引类似Oracle,会实际存储索引数据。

如果列存表没有创建索引,则可适当调大缓存的大小,即通过DWS管理控制台调大disk_cache_max_size

缓存状态

用户查询数据时,会优先到Disk Cache中查看数据是否已存在于本地磁盘,如果不存在则再去OBS读取数据,同时将数据缓存到本地磁盘,下次再读取这段数据时,即可在本地磁盘中读取到。使用Disk Cache可显著提升OBS数据的查询速度。

Disk Cache会默认使用主备两块硬盘作为缓存介质,通过查询以下参数查看相关信息:

通过查询视图pgxc_disk_cache_all_stats可以查看当前缓存的命中率以及各个DN磁盘的使用大小情况:

图2 pgxc_disk_cache_all_stats查询结果

缓存双写

开启缓存双写可以提升首次查询数据的性能,即用户在写数据到远端OBS的同时,将数据也写到本地Disk Cache上。当第一次读取数据时,可显著提升读取效率。用户可通过disk_cache_dual_write_option来设置是否开启缓存双写,参数包含三个设置选项:

  • none:表示不开启缓存双写。
  • hstore_only(默认值):表示只对hstore opt表,在delta merge时才开启缓存双写。
  • all:表示对普通v3表和hstore opt表都开启缓存双写。

缓存清理

通过函数pgxc_clear_disk_cache()可以将所有的Disk Cache清空。

集群空间不足与磁盘缓存空间调整

当集群出现资源容量不足时,对于Disk Cache中已使用了较大空间的集群来说,可通过缩小其空间来释放集群的磁盘空间,从而缓解资源不足状态;

通过调整disk_cache_max_size参数缩小Disk Cache的实际使用空间缓解集群空间不足:

具体示例如下,假设磁盘总容量为1000GB, disk_cache_max_size大小为500GB,通过视图pgxc_disk_cache_all_stats查询到实际占用450GB; 磁盘空间的总占用大小为900GB触发了资源剩余容量不足ThresholdReadRisk问题,在没有可清理的列存2.0表和索引资源的情况下,可将disk_cache_max_size的大小调整为300GB或者更小的数值来缓解空间不足问题,缺点是缩小Disk Cache可用规模后可能带来查询性能下降。

磁盘使用率告警判断如下:

  • 容量预警:磁盘空间占用或者文件描述符使用超过ThresholdReadOnly(默认80%);日志中会出现“Disk usage on the node %u has reached the risky threshold 80%”;
  • 容量不足:磁盘空间占用或者文件描述符使用超过ThresholdReadRisk(默认90%),触发集群只读;日志中会出现"Disk usage on the node %u has reached the read-only threshold 90%";
  • 容量严重不足:磁盘空间占用或者文件描述符使用超过ThresholdReadDanger(默认95%),会终止DN备实例和DN从备实例,重启DN主实例;日志中会出现"Disk usage on the node %u has reached the dangerous threshold 95%";

插入性能

Bucket存储

Bucket存储就是数据分片的一个手段,与分区技术类似,也就是具有相同属性值的数据存储在一起,这样带来的好处就是:存储和计算之间的映射调整比较容易,只有这样才能实现计算和存储的分层弹性,计算资源按需拉起。

比如8个Bucket,如果有2个DN节点,每个DN负责4个Bucket;如果有4个DN,每个DN负责2个Bucket。

图3 Bucket存储

入库优化

入库需要攒批,IO异步化。

攒批:是为了避免小CU,让后续的查询性能更好。

IO异步化:存算分离之后,写OBS相比写EVS时延要高约10倍,通过IO异步,优化读写性能。

  • 对于分区表,2.0表只需要进行partition攒批,3.0表相比2.0表多了Bucket攒批(相当于二级分区),可能会消耗更多的内存和磁盘。
  • 只有hash分布表需要分Bucket攒批。

入库的攒批开销与建议

开销

分区数目:#Np
每个节点Bucket数目:#Nb
RowGroup压缩前大小:#Nr
单个Bucket内存攒批最大大小:#Mb = max(partition_max_cache_size / partition_men_batch, 16M) = 16M(默认配置)

单并发攒批消耗: #Np * #Nb * #Nr
单并发攒批内存消耗: partition_max_cache_size, 默认2GB
单并发攒批磁盘消耗:#Np * #Nb * #Nr * 1.2(膨胀) - 内存消耗

假设一次copy数据,涉及1000个分区,#Nb≈10, 单条记录大小1K,攒批大小10000行
单并发攒批消耗: 1000 * 10 * 1K * 10000 * 1.2 = 120G

建议

  1. 应用层优化:最大的影响因素是分区数目,建议用单分区入库,单并发攒批消耗 120G->120M,就可以直接内存攒批了。
  2. 数据库内核参数优化:调整攒批大小,通过修改min_batch_rows进行控制,该参数为session级别,可以通过set语句设置当前session生效,或者通过修改配置文件让所有session生效。