GaussDB(DWS)性能调优系列基础篇三:衍化至繁之分布式计划详解

举报
along_2020 发表于 2020/10/22 21:39:58 2020/10/22
【摘要】 数据库分布式计划的主要特点在于执行算子在不同的节点(逻辑节点/物理节点)上执行,在保证最终执行结果正确性的前提下,如何更好的利用各节点资源是生成最优分布式计划以及性能调优均需面对的首要问题。

1. 前言

  • 适用版本:【8.1.1及以上】

前面两基础篇已经分别介绍了性能调优最基本的数据库命令ANALYZEEXPLAIN,本篇作为基础篇的最终篇,主要基于分布式数据库GaussDB(DWS)中产生的分布式计划多种多样的特点,补充对现有分布式计划种类及其性能优劣的详细介绍。

2. 分布式架构

说到分布式计划就不得不提到数据库的分布式架构,当前数据库分布式架构主要有Shared Nothing和Shared Disk两种:

Shared Disk:各处理单元共享数据磁盘系统但私有CPU和Memory资源,可通过增加节点来提高并行处理的能力,扩展能力较好,但是当存储器接口达到饱和的时候,增加节点并不能获得更高的性能。业界代表Oracle Rac。

Shared Nothing:各处理单元都有自己私有的CPU/内存/硬盘等,不存在共享资源,各处理单元之间通过协议通信,并行处理和扩展能力更好。业界代表Teradata。

GaussDB(DWS)基于Shared Nothing架构,继承了该架构良好的并行处理和扩展能力,但是也因数据分布存储不共享的特点,计划的生成需要面对数据倾斜、节点间数据交互等常见问题,生成的计划相对复杂。

3. 分布式计划

3.1 计划种类:

GaussDB(DWS)中当前主要存在三类分布式计划, FQS(fast query shipping)计划、Stream计划以及Remote-Query计划,其中前两类都可称之为下推计划,执行性能一般较好,而Remote-Query计划是前两类计划都无法生成情况下的最后选择,执行性能一般较差。各类计划介绍如下表,其中CN(Coordinator Node)表示数据库全局SQL解析优化节点又称协调节点,DN(Data Node)表示数据库数据存储节点也是计算单元。

计划种类 执行原理 子场景 适用场景
FQS计划 CN直接将原语句下发到DN,各DN单独执行,并将执行结果在CN上进行汇总。 整个SQL下发所有DN(典型场景) 各DN执行时无数据交互。
整个SQL下发单个DN(Lightproxy场景) 单DN能完全执行出结果,常见于TP点查场景。
Stream 计划 CN根据原语句生成计划并将计划下发给DN进行执行,各DN执行过程中使用Stream算子进行数据交互。 全部计划下发DN执行(全部下推) 各DN执行时有数据交互,常见于AP复杂语句场景。
部分计划下发DN执行(部分下推) 常见于子查询可下推,父查询存在不下推因素场景。
Remote-Query 计划 CN生成计划后,将部分原语句下发到DN,各DN单独执行,执行后将结果发送给CN,CN执行剩余计划。 仅当前单场景 不满足FQS和STREAM计划的极端场景,性能较差。

3.1.1 场景示例

前置:表定义和函数定义

示例1:FQS计划之典型场景

计划解读:join条件中t1.a和t2.a都是各自表的分布列,所以join能够匹配上的数据都在同一个DN,所以DN间不需要数据交换,原语句直接下发DN执行即可。

示例2:FQS计划之Lightproxy场景

计划解读:因t1.a是表t1的分布列,所以能够匹配上t1.a=1的数据只在某一个DN上,所以这个计划执行相比典型的FQS应该更简单,只在某一个DN上执行即可(例如上面计划所示,只在datanode5执行)。

示例3:Stream计划之全部下推场景

计划解读:join条件中t2.b列不是t2的分布列,所以需要对t2表按照a列做REDISTRIBUTE重分布,才能保证每个DN做join时能获取到所需的t1和t2表的对应数据。

示例4:Stream计划之部分下推场景

计划解读:unship_func为不能下推的函数,但是from后面的子查询可以下推,这种情况直接把子查询下推到了DN,执行完把数据返回给CN并在CN上调用unship_func计算出最终结果。

示例5:Remote-Query计划场景

计划解读:因unship_func不能下推,且不满足部分下推要求(子查询下推),所以只能发送基表扫描的语句到DN,将基表数据收集到CN上来计算。

3.1.2 小结

FQS计划和Stream计划下推到DN后,DN上能够执行除了scan之运算操作(例如join),但是Remote-Query计划中DN除了scan做不了其他任何运算,DN的计算资源没有得到充分利用,所以这类计划执行效率较前面几类差很多。同时,在前面介绍的这三大类计划中,AP复杂查询场景以Stream计划和Remote-Query计划最为常见,所以下面针对这两类计划进行进一步介绍。

3.2 Stream计划

说到Stream计划必须要说一说stream算子,stream算子是专门针对shared nothing型数据库数据不共享的特征所实现的一种算子,用于解决单个DN在运算过程中需要用到其他DN数据的情况,该算子的详细介绍参见《GaussDB(DWS)性能调优系列基础篇二:大道至简explain分布式计划》中的第一章节,下面以“两表join”和“双层agg”两个典型场景介绍stream计划生成过程中如何选择合适的stream算子,生成最优的stream计划。

3.2.1 两表join场景

例如,有两张表t1(a,b)和t2(a,b),t1按照t1.a进行hash分布,t2按照t2.a进行hash分布,当t1和t2进行join时,join列的不同将需要不同的stream算子:

JOIN 条件 需要 stream 可能生成的路径
t1.a = t2.a local(t1) join local(t2)
t1.a = t2.b local(t1) join redistribute(t2)或broadcast(t1) join local(t2)
t1.b = t2.a redistribute(t1) join local(t2)或local(t1) join broadcast(t2)
t1.b = t2.b redistribute(t1) join redistribute(t2)或broadcast(t1) join local(t2)或local(t1) join broadcast(t2)

其中关于redistribute和broadcast的选择主要是根据cost代价评估来选择,大致来说,大表join小表的场景,小表倾向做broadcast,大表倾向做redistribute,这样能够会最小化DN间通信交互的数据量,减少通信交互的开销,所以在调优过程中,如果通过explain发现大表在做broadcast,就要引起注意,可以通过调整分布列、planhint、SQL改写等方式修正计划以达到调优的目的,插播一条小广告,更多的计划调整方式会在后续“GaussDB(DWS)性能调优系列实战篇”中逐步的详细介绍

3.2.1 双层agg场景

例如,聚集操作group by列中不包含分布列时,意味着需要聚集的同组数据可能在不同的DN上,这时就需要通过redistribute按照group by列进行重分布,可能生成如下三种计划:

GaussDB(DWS)根据cost估算来决定选择上面哪个计划主要就两点原则:

1、DN上第一层agg整体返回的数据很少时,就不需要redistribute,而直接把数据返回CN做最终的agg即可。

2、DN上第一层agg整体返回的数据较多时,如果agg能过滤的数据也较多,则先做agg,否则先做redistribute。

简单点说,先做agg还是先做redistribute主要看先做agg是否能有效过滤数据,从而减少后续redistribute的数据量和传输代价,而在CN做二层agg还是DN做二层agg主要看第一层agg返回的整体数据量多少,因为这决定了Gather(数据从DN->CN)的代价。

3.2.3 小结

从前面两个典型场景可以看出,对stream算子broadcast/redistribute的选择,主要就是评估stream算子传输的数据量和代价,以减少stream传输数据量、降低通信消耗为基本原则。

3.3 Remote-Query计划

可能有人会有疑问,既然前面说Remote-Query是性能最差的计划,那为什么还要生成这种计划?主要是因为存在一些因素会导致执行算子无法在分散到各个DN分别执行而只能在CN上来统一执行,否则执行结果会出现错误。这些因素常被称之为不下推因素,最常见的不下推因素有不支持下推的函数和不支持下推的语法。

3.3.1 不下推因素之函数

数据库函数有两个关键属性provolatile和proshippable共同决定函数是否可以下推,其中provolatile属性的取值范围主要有IMMUTABLE、STABLE、VOLATILE三种:

IMMUTABLE:简单来讲,如果一个函数对于同样的输入,一定有相同的输出,那么这类函数就是IMMUTABLE的,例如绝大部分的字符串处理函数。

STABLE:如果一个函数的返回结果在一个SQL语句的调用过程中,结果是相同的,那么他就是STABLE的。例如时间相关的处理函数,这类函数都是STABLE的。

VOLATILE:如果一个函数的返回结果可能随着每一次的调用而返回不同的结果。例如nextval,random这种函数,每次调用结果都是不可预期的。

根据属性的介绍可以看到,IMMUTABLE/ STABLE属性的函数是比较稳定的,也就是对于相同的输出,输出的结果比较固定,这类函数分散到不同的DN执行,结果也不会有多大变化,而VOLATILE函数则不行,所以绝大部分的IMMUTABLE/ STABLE属性函数是可以下推的,VOLATILE函数则不能下推,具体下推情况如下表:

provolatile/proshippable true false
IMMUTABLE 可下推 可下推
STABLE 可下推 不可下推
VOLATILE 可下推 不可下推

需要注意的是当我们创建自定义函数时,默认的provolatile属性是volatile的, proshippable属性是false,也就是函数默认不下推,如果希望将函数定义为下推函数一定要清楚的理解这两个属性的含义,否则不能下推的函数下推了会导致执行结果的错误。

3.3.2 不下推因素之语法

无法下推的语法都有一定的特殊性,其中以“count(distinct expr)中的字段不支持重分布”场景为例:

例子中,distinct的b列为bool类型,不能做分布列,所以在DN通过局部的数据无法算出有效的distinct,所以这种场景不能下推,只能是DN把数据返回给CN统一执行,否则最终的计算结果会出现错误。

3.3.3 小结

从上面两个场景可以看出,产生Remote-Qeury计划一定是存在不下推因素导致,所以如果在业务调优场景发现Remote-Query计划,需要通过CN日志等方式第一时间找到不下推因素,然后通过改写、替换等方式将Remote-Query计划转变成Stream计划,以达到提高性能的目的。

4. 结语

数据库性能调优是一个系统工程,除了要熟练掌握数据库ANALYZE和EXPLAIN命令外,还需具备对数据库各类计划深度解读的能力,并结合操作系统I/O、内存等各方面的资源使用情况等进行整体分析,最终实现高效的性能调优。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。