使用实践:Fixed Plan加速SQL执行

本文涉及的产品
实时数仓Hologres,5000CU*H 100GB 3个月
简介: 本文将会介绍在Hologres中如何通过fixed plan加速SQL运行

Query执行过程

在Hologres中,当客户端发起一个SQL后,执行过程如下(以其中一个节点为例):

image.png

  1. Frontend(FE)节点对SQL进行解析和认证,并分发至执行引擎(Query Engine)的不同执行模块。
  2. 执行引擎(Query Engine)会根据SQL的特征走不同的执行路径。
  • 如果是点查/点写的场景,会跳过优化器(Query Optimizer,QO),直接分发至后端获取数据,减少数据传送链路,从而实现更优的性能。整个执行链路也叫Fixed Plan,点查(与HBase的KV查询)、点写场景会直接走Fixed Plan。
  • 如果是OLAP查询和写入场景:首先会由优化器(Query Optimizer,QO)对SQL进行解析,生成执行计划,在执行计划中会预估出算子执行Cost、统计信息、空间裁剪等。QO会通过生成的执行计划,决定使用HQE、PQE、SQE或者Hive QE对算子进行真正的计算。
  1. 执行引擎决定正确的执行计划,然后会通过存储引擎(Storage Engine,SE)进行数据获取,最后对每个Shard上的数据进行合并,返回至客户端。


Fixed Plan是Hologres独有的执行引擎优化方式,传统的SQL执行要经过优化器、协调器、查询引擎、存储引擎等多个组件,而Fixed Plan选择了短路径(Short-Cut)优化执行SQL,绕过了优化器、协调器、部分查询引擎的开销。通过Fixed FrontEnd直接对接Fixed Query Engine,实现SQL执行效率的成倍提升,是支持高吞吐实时写入,高并发查询的关键优化方法。


可以被Fixed Plan选中的SQL需要符合一定的条件,以及一定的参数配置,下面将会介绍这些配置参数以及SQL条件。


相关GUC参数

GUC列表

以下为fixed plan需要用到的参数配置:

说明:以下Fixed Plan相关参数,已经在Holo Client中默认打开,session级别生效。

GUC名称

适用场景

默认值

hg_experimental_enable_fixed_dispatcher

查看实例的fixed plan是否打开。

on

hg_experimental_enable_fixed_dispatcher_for_multi_values

支持insert on conflict多行记录的Fixed Plan写入。建议客户端session级别打开。

注意:不保证原子性,即一次性写入多条的时候,如果没报错就是全部正常写入了,如果报错了只会报一条的错误,有可能全部没写入也有可能部分写入了部分没写入,没有写入的部分会将错误反馈给上层应用端,由应用端进行重试。

off

hg_experimental_enable_fixed_dispatcher_autofill_series

支持含有serial列的Fixed Plan写入。

建议客户端session级别打开。

off

hg_experimental_enable_fixed_dispatcher_for_update

支持更新(update)场景的Fixed Plan更新。

建议客户端session级别打开。

off

hg_experimental_enable_fixed_dispatcher_for_delete

支持删除(delete)场景的Fixed Plan删除。建议客户端session级别打开。

off

hg_experimental_enable_fixed_dispatcher_for_scan

支持PrefixScan场景的Fixed Plan查询。

说明:PrefixScan是指多列主键,查询条件只给前面几列主键

off

hg_experimental_enable_bhclient_cache_on_session

是否用cached on session模式,默认是cached on fe模式。

cache on session 和cache on fe的区别:

  • on session是每个连接拥有自己的writer/reader,单连接的吞吐更好,但启动会更慢(每个表第一次进行读/写需要有启动时间)
  • on fe是fe(Frontend)上所有连接共享writer/reader,连接断开后writer/reader不会关闭,所以总体上没有启动时间

off

GUC使用

1)查看GUC是否开启

通过show命令查看GUC是否开启:

show <GUC_name>;--示例:查看实例级别是否开启fixed planshow hg_experimental_enable_fixed_dispatcher;


2)session级别开启GUC

通过set命令可以在session级别设置GUC参数。session级别的参数只在当前session生效,当连接断开之后,将会失效,建议加在SQL前一起执行。

语法示例如下。

set<GUC_name>=<values>;

GUC_name为GUC参数的名称,values为GUC参数的值。

使用示例如下。

--insert on conflict多行记录支持Fixed Plan写入set hg_experimental_enable_fixed_dispatcher_for_multi_values =on;


3)数据库级别

可以通过alter database xx set xxx命令来设置DB级别的GUC参数执,执行完成后在整个DB级别生效,设置完成后当前连接需要重新断开连接才能生效。新建DB不会生效,需要重新手动设置。语法示例如下。

alter database <db_name>set<GUC_name>=<values>;

其中:db_name为数据库名称,GUC_name为GUC参数的名称,values为GUC参数的值。

使用示例如下:

--DB级别开启fixed planalter database <db_name>set hg_experimental_enable_fixed_dispatcher =on;

对数据类型的要求

  • 表的每一列都不能是MONEY或MONEY ARRAY
  • DML(INSERT/UPDATE/DELETE)列 和 SELECT(select 的 target列和where里的列都要满足)列支持的类型:
  • BOOLEAN(别名BOOL)
  • SMALLINT
  • INTEGER(别名INT或INT4)
  • BIGINT(别名INT8)
  • FLOAT(别名FLOAT4)
  • DOUBLE PRECISION(别名FLOAT8)
  • CHAR(n)
  • VARCHAR(n)
  • BYTEA
  • JSON和JSONB
  • TEXT(别名VARCHAR)
  • TIMESTAMP WITH TIME ZONE(别名TIMESTAMPTZ)
  • DATE
  • TIMESTAMP
  • DECIMAL(别名NUMERIC)
  • ROARINGBITMAP
  • 数组类型
  • boolean[]
  • smallint[]
  • int4[]
  • int8[]
  • float4[]
  • float8[]
  • char(n)[]
  • varchar(n)[]
  • text[]

INSERT场景

Insert表达式

fixed plan支持以下insert表达式:

---写入单行insertintotable(col1,col2,col3..)values(?,?,?..)on conflict xxx;---写入多行insertintotable(col1,col2,col3..)values(?,?,?..),(?,?,?..)on conflict xxx;
  • 支持内表,不支持外表
  • 支持分区子表,不支持写分区主表(V1.3版本开始支持写分区主表)

Insert on conflict单行

支持场景:

  1. 支持没有on conflict表达式
  2. 支持on conflict do nothing表达式
  3. 支持on conflict do update时,必须update所有insert的非PK(Primary Key,主键,以下简称PK)的列,pk是否update都可以,并且只能是col = excluded.col方式更新
  4. 使用示例:
begin;createtable test_insert_oneline(  pk1 int,  pk2 int,  col1 int,  col2 int,  primary key(pk1, pk2));commit;--update所有非PK列,可以Fixed Planinsertinto test_insert_oneline values(1,2,3,4)on conflict(pk1,pk2) do updateset col1 = excluded.col1, col2 = excluded.col2;--update所有列(包含PK和非PK),可以Fixed Planinsertinto test_insert_oneline values(1,2,3,4)on conflict(pk1,pk2) do updateset col1 = excluded.col1, col2 = excluded.col2, pk1 = excluded.pk1, pk2 = excluded.pk2;--必须update所有非pk的要insert的列,这个例子不包含col2,因此不能Fixed Planinsertinto test_insert_oneline values(1,2,3,4)on conflict(pk1,pk2) do updateset col1 = excluded.col1;--必须是set col = excluded.col方式更新,因此不能Fixed Planinsertinto test_insert_oneline values(1,2,3,4)on conflict(pk1,pk2) do updateset col1 = excluded.col1, col2 =5;

Insert on conflict多行

1)insert on conflict多行时,表达式如下:

set hg_experimental_enable_fixed_dispatcher_for_multi_values =on;insertintotable(col1,col2,col3..)values(?,?,?..),(?,?,?..)on conflict xxx;
  • 需要配置GUC hg_experimental_enable_fixed_dispatcher_for_multi_values为on
  • 不保证原子性,即一次性写入多条的时候,如果没报错就是全部正常写入了,如果报错了有可能全部没写入也有可能部分写入了部分没写入


2)写入多行另一种写法:

set hg_experimental_enable_fixed_dispatcher_for_multi_values =on;insertintotableselectunnest(ARRAY[true,false,true]::bool[]),unnest(ARRAY[1,2,3]::int4[]),unnest(ARRAY[1.11,2.222,3]::float4[])on conflict xxx;
  • 需要配置hg_experimental_enable_fixed_dispatcher_for_multi_values为on
  • 写入的列不能是数组类型
  • unnest里ARRAY必须显式cast为对应列类型的数组类型
  • 使用示例:
begin;createtable test_insert_multiline(  pk1 int8,  col1 float4,  primary key(pk1));commit;--支持Fixed Planset hg_experimental_enable_fixed_dispatcher_for_multi_values =on;insertinto test_insert_multiline select unnest(ARRAY[1,2,3]::int8[]), unnest(ARRAY[1.11,2.222,3]::float4[])on conflict do nothing;--unnest里ARRAY没有显式cast,不支持Fixed Planinsertinto test_insert_multiline select unnest(ARRAY[1,2,3]), unnest(ARRAY[1.11,2.222,3])on conflict do nothing;--第一列是int8,所以应该cast为int8[],这里例子是int4[],因此不支持Fixed Planinsertinto test_insert_multiline select unnest(ARRAY[1,2,3]::int4[]), unnest(ARRAY[1.11,2.222,3]::float4[])on conflict do nothing;

局部更新场景

Hologres支持通过主键,对表的部分列做更新,Fixed Plan同样可以支持局部更新的,需要满足以下条件

  • Insert的列需要与update的列一一对应,包括数量和顺序
  • 只能是col = excluded.col方式更新

Default列

表中含有default列时,写入时能走fixed plan的条件:

  • 插入单条时支持
  • 插入多条时,需要 实例版本>=1.1.36版本,若低于请升级。同时需要设置GUC参数hg_experimental_enable_fixed_dispatcher_for_multi_values为on
  • 有default列的表,不支持insert on conflict表达式的Fixed Plan
  • 使用示例:
begin;createtable test_insert_default(  pk1 int,  col1 int default 99,  primary key(pk1));commit;--支持Fixed Planinsertinto test_insert_default(pk1)values(1);--需要 V1.1.36+版本支持set hg_experimental_enable_fixed_dispatcher_for_multi_values =on;insertinto test_insert_default(pk1)values(1),(2),(3);

Serial列

表带有自增序列serial时,支持单条或者多条写入时走fixed plan,使用条件如下:

  • 需要配置GUC hg_experimental_enable_fixed_dispatcher_autofill_series为on
  • 写多行时需要配置GUC参数hg_experimental_enable_fixed_dispatcher_for_multi_values为on
  • 有serial列的表,不支持insert on conflict表达式的Fixed Plan
begin;createtable test_insert_serial(  pk1 int,  col1 serial,  primary key(pk1));commit;--支持Fixed Planset hg_experimental_enable_fixed_dispatcher_autofill_series =on;insertinto test_insert_serial (pk1)values(1);--支持Fixed Planset hg_experimental_enable_fixed_dispatcher_autofill_series =on;set hg_experimental_enable_fixed_dispatcher_for_multi_values =on;insertinto test_insert_serial (pk1)values(1),(2),(3);


UPDATE场景

Update表达式

update时能走fixed plan的表达式如下:

set hg_experimental_enable_fixed_dispatcher_for_update =on;updatetableset col1 = ?, col2 =? where pk1 =? and pk2 =?;
  • 需要配置GUC hg_experimental_enable_fixed_dispatcher_for_update 为on。
  • 支持内表,不支持外表
  • 不能是分区主表,支持分区子表
  • 表必须有主键(pk)

Update的使用条件

updtae场景支持的情况如下:

  • set 的列不能是pk
  • where 里有且只有全部pk
  • 可以用pk in (?,?,?) 或 pk = ANY() 一次性改多条e.g. pk1 in (1,2) and pk2 = any('{3,4}') and pk3 = 5 <=> 改(1,3,5),(1,4,5),(2,3,5),(2,4,5) 四条
  • where里同一列只能有一个条件(一模一样的视为一个条件)
  • 使用示例:
begin;createtable test_update(  pk1 int,  pk2 int,  col1 int,  col2 int,  primary key(pk1, pk2));commit;--支持Fixed Planset hg_experimental_enable_fixed_dispatcher_for_update =on;update test_update set col1 =1, col2 =2where pk1 =3and pk2 =4;--支持Fixed Planset hg_experimental_enable_fixed_dispatcher_for_update =on;update test_update set col1 =1where pk1 =3and pk2 =4;--支持Fixed Planset hg_experimental_enable_fixed_dispatcher_for_update =on;update test_update set col1 =1, col2 =2where pk1 in(1,2)and pk2 = any('{3,4}');--pk1多个过滤条件,不支持Fixed Planupdate test_update set col1 =1, col2 =2where pk1 =3and pk1 =4;--pk1多个过滤条件,不支持Fixed Planupdate test_update set col1 =1, col2 =2where pk1 in(1,2)and pk1 =1;--pk1多个过滤条件,但过滤条件相同,支持Fixed Planset hg_experimental_enable_fixed_dispatcher_for_update =on;update test_update set col1 =1, col2 =2where pk1 in(1,2)and pk1 in(1,2)and pk2 =4;


DELETE场景

Delete表达式

set hg_experimental_enable_fixed_dispatcher_for_delete =on;deletefromtablewhere pk1 =? and pk2 =? and pk3 =?;
  • 需要配置GUC hg_experimental_enable_fixed_dispatcher_for_delete为on
  • 支持内表,不支持外表
  • 不支持分区主表,支持分区子表
  • 表必须有pk

Delete的使用条件

delete场景支持的情况如下:

  • where 里有且只有全部pk
  • 可以用pk in (?,?,?) 或 pk = ANY() 一次性删多条e.g. pk1 in (1,2) and pk2 = any('{3,4}') and pk3 = 5 <=> 删(1,3,5),(1,4,5),(2,3,5),(2,4,5) 四条
  • 同一列只能有一个条件(一模一样的视为一个条件)
  • 使用示例:
begin;createtable test_delete(  pk1 int,  pk2 int,  col1 int,  col2 int,  primary key(pk1, pk2));commit;--支持Fixed Plan,更多场景与Update样例一致set hg_experimental_enable_fixed_dispatcher_for_delete =on;deletefrom test_delete where pk1 =1and pk2 =2;


SELECT场景

Select表达式

select col1,col2,col3,...fromtablewhere pk1 =? and pk2 =? and pk3 =?;
  • 支持内表,不支持外表
  • 不支持分区主表,支持分区子表
  • 表必须有pk

点查(key/value)场景

点查场景支持的情况如下:如下:

  • where 里有且只有全部pk
  • 可以用pk in (?,?,?) 或 pk = ANY() 一次性查多条e.g. pk1 in (1,2) and pk2 = any('{3,4}') and pk3 = 5 <=> 查(1,3,5),(1,4,5),(2,3,5),(2,4,5) 四条
  • 同一列只能有一个条件(一模一样的视为一个条件)
  • 如果有limit, limit的值必须 >0
begin;createtable test_select(  pk1 int,  pk2 int,  col1 int,  col2 int,  primary key(pk1, pk2));commit;--支持Fixed Plan,更多场景条件与Update样例一致select*from test_select where pk1 =1and pk2 =2;

PrefixScan场景

PrefixScan场景是指表有多个主键,查询时按照左匹配原则只查几列主键。查询如下:

set hg_experimental_enable_fixed_dispatcher_for_scan =on;select col1,col2,col3,...fromtablewhere pk1 =? and pk2 =?;select col1,col2,col3,...fromtablewhere pk1 =? and pk2 >? and pk 2<?;--从1.1.48版本开始支持pk最后一列条件为rangeselect col1,col2,col3,...fromtablewhere pk1 =? and pk2  between? and?;--从1.1.48版本开始支持pk最后一列条件为range


  • 需要配置GUC hg_experimental_enable_fixed_dispatcher_for_scan为on,且V1.1.24+版本
  • PrefixScan从1.1.48版本开始支持pk最后一列条件为range。

说明:prefixScan一次性返回所有结果行,如果结果的字节数大于hg_experimental_fixed_scan_bytesize_limit会报错 scan result size larger than fixed scan size limit,可以通过配置hg_experimental_fixed_scan_bytesize_limit设置更符合场景的值,默认1048576,即1MB。

  • 表必须有distribution key


PrefixScan的使用条件如下:

  • where里有且只有pk的prefix。prefix定义: 若pk为(pk1,pk2,pk3)    (pk1),(pk1,pk2)为prefix
  • where里必须包含所有的distribution key
  • e.g. 若表pk为(pk1,pk2,pk3,pk4), distribution key 为 'pk1,pk3',支持情况如下:
begin;createtable test_select_prefix(  pk1 int,  pk2 int,  pk3 int,  pk4 int,  primary key(pk1, pk2,pk3,pk4));call set_table_property('test_select_prefix','distribution_key','pk1,pk3');commit;--没有包含所有distribution key,不能走fixed planselect*from test_select_prefix where pk1 =? and pk2 =?;--不是pk的prefix,不能走fixed planselect*from test_select_prefix where pk1 =? and pk3 =?;--可以走fixed planset hg_experimental_enable_fixed_dispatcher_for_scan =on;select*from test_select_prefix where pk1 =? and pk2 =? and pk3 =?;


  • 可以用pk in (?,?,?) 或 pk = ANY() 一次性查多条

pk1 in (1,2) and pk2 = 3 <=> scan(1,3),(2,3)两组

pk2 =any('{3,4}') and pk1 in (1,2) <=> scan(1,3),(1,4),(2,3),(2,4)四组

  • 同一列只能有一个条件(一模一样的视为一个条件)
  • 如果有limit,limit的值必须 >0
  • 使用示例如下:
begin;createtable test_scan(  pk1 int,  pk2 int,  pk3 int,  col1 int,  primary key(pk1, pk2, pk3));CALL SET_TABLE_PROPERTY ('test_scan','distribution_key','pk1,pk2');commit;INSERTINTO test_scan values(1,2,3,4);--支持Fixed Planset hg_experimental_enable_fixed_dispatcher_for_scan =on;select*from test_scan where pk1 =1and pk2 =2;--支持Fixed Planset hg_experimental_enable_fixed_dispatcher_for_scan =on;select*from test_scan where pk1 =1and pk2 in(2,3);--支持Fixed Planset hg_experimental_enable_fixed_dispatcher_for_scan =on;select*from test_scan where pk1 = ANY('{3,4}')and pk2 in(2,3);--支持fixed plan,pk最后一列是range条件,需要1.1.48及以上版本支持set hg_experimental_enable_fixed_dispatcher_for_scan =on;select*from test_scan where pk1 =1and pk2 >1and pk2 <3;--支持fixed plan,pk最后一列是range条件,需要1.1.48及以上版本支持set hg_experimental_enable_fixed_dispatcher_for_scan =on;select*from test_scan where pk1 =1and pk2 between1and3;--不包含所有的distribution key,不支持Fixed Planselect*from test_scan where pk1 =1;--不符合主键前缀Prefix,不支持Fixed Planselect*from test_scan where pk2 =2;

验证Fixed Plan

通过FixedPlan执行的更新类SQL,在控制台的实时导入RPS面板中会显示为SDK类型,包括Insert、Update和Delete类型的操作。建议实时写入类Insert、Update、Delete都尽量优化为Fixed Plan方案,改善数据更新的效率。

也可以通过查看SQL执行计划(explain sql)验证是否符合Fixed Plan,如果返回的执行计划中有FixedXXXNode,既表示触发了Fixed Plan。如未生成FixedXXXNode 执行计划,请对照 上文支持场景 对照是否满足条件。

image.png

性能调优

在某些场景上若是已经开启Fixed Plan但还需要做性能调优时,可选择如下方式。

  • Hologres V1.1.49版本开始针对Fixed Plan点查场景进行了优化,在大规模点查的情况下提升了30%以上吞吐。若有需要请升级实例至V1.1.49及以上版本。
  • 客户端合理的攒批(使用Holo Client会自动攒批),即一次执行SQL命令的数量,实践证明数量为512或者512的倍数性能会更好。


了解Hologres:https://www.aliyun.com/product/bigdata/hologram

合集.png

相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
相关文章
|
3月前
|
SQL 存储 API
Flink实践:通过Flink SQL进行SFTP文件的读写操作
虽然 Apache Flink 与 SFTP 之间的直接交互存在一定的限制,但通过一些创造性的方法和技术,我们仍然可以有效地实现对 SFTP 文件的读写操作。这既展现了 Flink 在处理复杂数据场景中的强大能力,也体现了软件工程中常见的问题解决思路——即通过现有工具和一定的间接方法来克服技术障碍。通过这种方式,Flink SQL 成为了处理各种数据源,包括 SFTP 文件,在内的强大工具。
184 15
|
2月前
|
SQL 关系型数据库 MySQL
Go语言项目高效对接SQL数据库:实践技巧与方法
在Go语言项目中,与SQL数据库进行对接是一项基础且重要的任务
83 11
|
2月前
|
SQL 存储 关系型数据库
添加数据到数据库的SQL语句详解与实践技巧
在数据库管理中,添加数据是一个基本操作,它涉及到向表中插入新的记录
|
2月前
|
SQL 关系型数据库 数据库
SQL数据库:核心原理与应用实践
随着信息技术的飞速发展,数据库管理系统已成为各类组织和企业中不可或缺的核心组件。在众多数据库管理系统中,SQL(结构化查询语言)数据库以其强大的数据管理能力和灵活性,广泛应用于各类业务场景。本文将深入探讨SQL数据库的基本原理、核心特性以及实际应用。一、SQL数据库概述SQL数据库是一种关系型数据库
81 5
|
2月前
|
SQL 开发框架 .NET
ASP连接SQL数据库:从基础到实践
随着互联网技术的快速发展,数据库与应用程序之间的连接成为了软件开发中的一项关键技术。ASP(ActiveServerPages)是一种在服务器端执行的脚本环境,它能够生成动态的网页内容。而SQL数据库则是一种关系型数据库管理系统,广泛应用于各类网站和应用程序的数据存储和管理。本文将详细介绍如何使用A
77 3
|
2月前
|
SQL 消息中间件 分布式计算
大数据-143 - ClickHouse 集群 SQL 超详细实践记录!(一)
大数据-143 - ClickHouse 集群 SQL 超详细实践记录!(一)
87 0
|
2月前
|
SQL 大数据
大数据-143 - ClickHouse 集群 SQL 超详细实践记录!(二)
大数据-143 - ClickHouse 集群 SQL 超详细实践记录!(二)
66 0
|
4月前
|
SQL 流计算
Flink SQL 在快手实践问题之由于meta信息变化导致的state向前兼容问题如何解决
Flink SQL 在快手实践问题之由于meta信息变化导致的state向前兼容问题如何解决
51 1
|
4月前
|
SQL 安全 流计算
Flink SQL 在快手实践问题之Group Window Aggregate 中的数据倾斜问题如何解决
Flink SQL 在快手实践问题之Group Window Aggregate 中的数据倾斜问题如何解决
84 1
|
4月前
|
SQL 设计模式 数据处理
Flink SQL 在快手实践问题之状态兼容的终极方案特点内容如何解决
Flink SQL 在快手实践问题之状态兼容的终极方案特点内容如何解决
26 0

热门文章

最新文章