首页 > 数据库 > > 典型SQL调优点-语句下推调优
目前,GaussDB 200优化器在分布式框架下制定语句的执行策略时,有三种执行计划方式:生成下推语句计划、生成分布式执行计划、生成发送语句的分布式执行计划。
在第3种策略中,要将大量中间结果从DN发送到CN,并且要在CN运行不能下推的部分语句,会导致CN成为性能瓶颈(带宽、存储、计算等)。在进行性能调优的时候,应尽量避免只能选择第3种策略的查询语句。
执行语句不能下推是因为语句中含有不支持下推的函数或者不支持下推的语法。一般都可以通过等价改写规避执行计划不能下推的问题。
执行计划是否下推可以依靠如下方法快速判断:
SET enable_fast_query_shipping = off;查看执行计划。
如果执行计划中有Data Node Scan节点,那么此执行计划为不可下推的执行计划;如果执行计划中有Streaming节点,那么计划是可以下推的。
例如如下业务SQL:
select
count(ss.ss_sold_date_sk order by ss.ss_sold_date_sk)c1
from store_sales ss, store_returns sr
where
sr.sr_customer_sk = ss.ss_customer_sk;
执行计划如下,可以看出此SQL语句不能下推。
QUERY PLAN
--------------------------------------------------------------------------
Aggregate
-> Hash Join
Hash Cond: (ss.ss_customer_sk = sr.sr_customer_sk)
-> Data Node Scan on store_sales "_REMOTE_TABLE_QUERY_"
Node/s: All datanodes
-> Hash
-> Data Node Scan on store_returns "_REMOTE_TABLE_QUERY_"
Node/s: All datanodes
(8 rows)
以如下三个表定义说明不支持下推的SQL语法。
CREATE TABLE CUSTOMER1
(
C_CUSTKEY BIGINT NOT NULL
, C_NAME VARCHAR(25) NOT NULL
, C_ADDRESS VARCHAR(40) NOT NULL
, C_NATIONKEY INT NOT NULL
, C_PHONE CHAR(15) NOT NULL
, C_ACCTBAL DECIMAL(15,2) NOT NULL
, C_MKTSEGMENT CHAR(10) NOT NULL
, C_COMMENT VARCHAR(117) NOT NULL
)
DISTRIBUTE BY hash(C_CUSTKEY);
CREATE TABLE test_stream(a int,b float); --float不支持重分布
CREATE TABLE sal_emp ( c1 integer[] ) DISTRIBUTE BY replication;
explain update customer1 set C_NAME = 'a' returning c_name;
QUERY PLAN
------------------------------------------------------------------
Update on customer1 (cost=0.00..0.00 rows=30 width=187)
Node/s: All datanodes
Node expr: c_custkey
-> Data Node Scan on customer1 "_REMOTE_TABLE_QUERY_" (cost=0.00..0.00 rows=30 width=187)
Node/s: All datanodes
(5 rows)
explain verbose select count ( c_custkey order by c_custkey) from customer1;
QUERY PLAN
------------------------------------------------------------------ Aggregate (cost=2.50..2.51 rows=1 width=8)
Output: count(customer1.c_custkey ORDER BY customer1.c_custkey)
-> Data Node Scan on customer1 "_REMOTE_TABLE_QUERY_" (cost=0.00..0.00 rows=30 width=8)
Output: customer1.c_custkey
Node/s: All datanodes
Remote query: SELECT c_custkey FROM ONLY public.customer1 WHERE true
(6 rows)
explain verbose select count(distinct b) from test_stream;
QUERY PLAN
------------------------------------------------------------------ Aggregate (cost=2.50..2.51 rows=1 width=8)
Output: count(DISTINCT test_stream.b)
-> Data Node Scan on test_stream "_REMOTE_TABLE_QUERY_" (cost=0.00..0.00 rows=30 width=8)
Output: test_stream.b
Node/s: All datanodes
Remote query: SELECT b FROM ONLY public.test_stream WHERE true
(6 rows)
explain verbose select distinct on (c_custkey) c_custkey from customer1 order by c_custkey;
QUERY PLAN
------------------------------------------------------------------ Unique (cost=49.83..54.83 rows=30 width=8)
Output: customer1.c_custkey
-> Sort (cost=49.83..52.33 rows=30 width=8)
Output: customer1.c_custkey
Sort Key: customer1.c_custkey
-> Data Node Scan on customer1 "_REMOTE_TABLE_QUERY_" (cost=0.00..0.00 rows=30 width=8)
Output: customer1.c_custkey
Node/s: All datanodes
Remote query: SELECT c_custkey FROM ONLY public.customer1 WHERE true
(9 rows)
explain select * from test_stream t1 full join test_stream t2 on t1.a=t2.b;
QUERY PLAN
------------------------------------------------------------------ Hash Full Join (cost=0.38..0.82 rows=30 width=24)
Hash Cond: ((t1.a)::double precision = t2.b)
-> Data Node Scan on test_stream "_REMOTE_TABLE_QUERY_" (cost=0.00..0.00 rows=30 width=12)
Node/s: All datanodes
-> Hash (cost=0.00..0.00 rows=30 width=12)
-> Data Node Scan on test_stream "_REMOTE_TABLE_QUERY_" (cost=0.00..0.00 rows=30 width=12)
Node/s: All datanodes
(7 rows)
explain verbose select array[c_custkey,1] from customer1 order by c_custkey;
QUERY PLAN
------------------------------------------------------------------ Sort (cost=49.83..52.33 rows=30 width=8)
Output: (ARRAY[customer1.c_custkey, 1::bigint]), customer1.c_custkey
Sort Key: customer1.c_custkey
-> Data Node Scan on "__REMOTE_SORT_QUERY__" (cost=0.00..0.00 rows=30 width=8)
Output: (ARRAY[customer1.c_custkey, 1::bigint]), customer1.c_custkey
Node/s: All datanodes
Remote query: SELECT ARRAY[c_custkey, 1::bigint], c_custkey FROM ONLY public.customer1 WHERE true ORDER BY 2
(7 rows)
序号 | 场景 | 不下推原因 |
|---|---|---|
1 | 包含外表、HDFS表的查询场景 | LOG: SQL can't be shipped, reason: RecursiveUnion contains HDFS Table or ForeignScan is not shippable(LOG为CN日志中打印的不下推原因,下同) 外表、HDFS表,当前版本暂不支持下推。 |
2 | 多nodegroup场景 | LOG: SQL can't be shipped, reason: With-Recursive under multi-nodegroup scenario is not shippable 基表存储nodegroup不相同,或者计算nodegroup与基表不相同,当前版本暂不支持下推。 |
3 | | LOG: SQL can't be shipped, reason: With-Recursive does not contain "ALL" to bind recursive & none-recursive branches UNION不带ALL,需要去重。 |
4 | | LOG: SQL can't be shipped, reason: With-Recursive contains system table is not shippable 基表中有系统表。 |
5 | | LOG: SQL can't be shipped, reason: With-Recursive contains only values rte is not shippable 基表扫描只有VALUES子句,仅在CN上即可完成执行。 |
6 | | LOG: SQL can't be shipped, reason: With-Recursive recursive term correlated only is not shippable 相关子查询的关联条件仅在递归部分,非递归部分无关联条件。 |
7 | | LOG: SQL can't be shipped, reason: With-Recursive contains conflict distribution in none-recursive(Replicate) recursive(Hash) 非递归部分带limit为Replicate计划,递归部分为 Hash计划,计划存在冲突。 |
8 | | LOG: SQL can't be shipped, reason: Recursive CTE references recursive CTE "cte" 多层Recursive嵌套,即recursive的递归部分又嵌套另一个recursive查询。 |
首先介绍函数的易变性。在GaussDB 200中-共分三种形态:
表示该函数在给出同样的参数值时总是返回同样的结果。
表示该函数不能修改数据库,对相同参数值,在同一次表扫描里,该函数的返回值不变,但是返回值可能在不同SQL语句之间变化。
表示该函数值可以在一次表扫描内改变,因此不会做任何优化。
函数易变性可以查询pg_proc的provolatile字段获得,i代表IMMUTABLE,s代表STABLE,v代表VOLATILE。另外,在pg_proc中的proshippable字段,取值范围为t/f/NULL,这个字段与provolatile字段一起用于描述函数是否下推。
对于用户自定义函数,可以在创建函数的时候指定provolatile和proshippable属性的值,详细请参考CREATE FUNCTION。
对于函数不能下推的场景:
对于自定义函数,如果对于确定的输入,有确定的输出,则应将函数定义为immutable类型。
利用TPCDS的销售信息举个例子,比如我们要写一个函数,获取商品的打折情况,需要一个计算折扣的函数,我们可以将这个函数定义为:
CREATE FUNCTION func_percent_2 (NUMERIC, NUMERIC) RETURNS NUMERIC
AS 'SELECT $1 / $2 WHERE $2 > 0.01'
LANGUAGE SQL
VOLATILE;
执行下列语句:
SELECT func_percent_2(ss_sales_price, ss_list_price)
FROM store_sales;
其执行计划为:

可见,func_percent_2并没有被下推,而是将ss_sales_price和ss_list_price收到CN上,再进行计算,消耗大量CN的资源,而且计算缓慢。
由于该自定义函数对确定的输入有确定的输出,如果将该自定义函数改为:
CREATE FUNCTION func_percent_1 (NUMERIC, NUMERIC) RETURNS NUMERIC
AS 'SELECT $1 / $2 WHERE $2 > 0.01'
LANGUAGE SQL
IMMUTABLE;
执行语句:
SELECT func_percent_1(ss_sales_price, ss_list_price)
FROM store_sales;
其执行计划为:

可见函数func_percent_1被下推到DN执行,提升了执行效率(TPCDS 1000X,3CN18DN,查询效率提升100倍以上)。
请参考案例:使排序下推。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69959250/viewspace-3118130/ ,如需转载,请注明出处,否则将追究法律责任。