矢量计算执行器——VE引擎
VE引擎使用说明¶
简介¶
什么是执行引擎¶
日志 DDL
^ /
\ /
\ /
\ V
sql语句-->查询编译器---------> 执行引擎 <-------> 存储管理器
生成计划 ^ ^ 读/写
/ \
/ \
V V
并发控制 缓冲区
执行引擎在数据库管理系统中和许多组件都有交互。它的主要职责是
-
执行上游的生成的执行计划的每一步
-
和下游的存储管理器交互读写(实际系统中还需要经过索引、缓存等中间环节与存储管理器交互)
-
生成执行日志
-
缓冲区管理(内存、磁盘)
-
DDL语句处理
-
并发控制 保证事务的ACID特性
本章主要介绍前面3个特性
什么是向量化执行引擎¶
在seabox数据库中,有2个执行引擎
-
基于行的执行引擎(默认):一次处理一行数据, 是大多数数据库管理系统使用的引擎
-
向量化执行引擎: vector engine 简称VE引擎
VE引擎相对默认引擎,简单的说就是一次处理多行数据。
关系数据库存储的数据在逻辑上就像是一个矩阵:
a b c d e
1 /. . . . .\
2 |. . . . .|
3 T = |. . . . .|
4 |. . . . .|
5 |. . . . .|
6 \. . . . ./
如果计算
select sum(a) from table;
只需要取出“向量a” 然后进行运算即可,VE引擎的向量化执行是指将“向量a”分割成多个子向量进行运算,向量a可以是多列:
a b c d e 第一次取向量
1 /* . . . .\
2 |* . . . .| / \
3 T = |* . . . .| | 1 |
4 |. . . . .| a = | 2 |
5 |. . . . .| | 3 |
6 \. . . . ./ \ /
a b c d e 第二次取向量
1 /. . . . .\
2 |. . . . .| / \
3 T = |. . . . .| | 4 |
4 |* . . . .| a = | 5 |
5 |* . . . .| | 6 |
6 \* . . . ./ \ /
只要有可能,VE引擎的操作都是基于向量进行分派的,而不是单个的值
为什么引入VE引擎¶
简单的说就是比默认引擎快,一般快2-10倍,其原理是
- 减少函数调用开销
每个操作在执行时都有一个复杂的函数调用栈,按行处理时,每行都要遍历一遍调用栈;使用VE引擎时一个调用栈处理多行数据,提高了效率
- 利用CPU的SIMD特性
SIMD特性就是专门为一次处理多个数据而生的,典型的如SSE,AVX等指令
- 利用CPU缓存
VE引擎会控制向量的长度,使之尽量能被CPU cache住。在VE引擎中一个向量控制在几k至几十k字节,在列存储上一般是几千到几万行。
- 支持多线程并行执行
可以指定并行度,最大的有效并行度为cpu核数。一般指定8并行可以比串行执行快4-5倍
- 支持hash聚集等新的执行算法
可见VE引擎除了支持“向量化计算”外,还通过引入并行执行,新的算法,新的函数等来加快执行。
注意:有些函数或操作VE引擎不支持,在后面会详细介绍
行存与列存¶
seabox数据库管理系统默认使用“列存储”,数据是按列组织的,对于VE引擎一次取一列非常方便,也是VE引擎的主要应用场景。
seabox数据库管理系统还可以建立“行存储”的表,此时VE引擎仍然有优势,因为虽然和存储管理器交互时需要多一次按列提取的操作,一旦数据读取完毕后,后续的每一步计算仍可以向量化进行,并且VE引擎还执行多线程并行执行。
VE引擎参数¶
参数名 | 默认 | 含义 |
---|---|---|
ve_enable | off | ve引擎开关 |
set ve_enable = on;
参数名 | 默认 | 含义 |
---|---|---|
ve_parallel_degree | 1 | 并行度,最多设置为cpu个数,一般设置1-8 |
-
比如设置并行度4,在执行sql时会看到最多有4个cpu核满载。
-
对于集群,一个sql可能会拆解成多个slice,并行度4时就会看到最多有 4*slice个数的cpu核满载
-
注意区别并行度与并发,并行度是一条sql内的并行优化,并发是指多条sql同时再一个实例上运行
参数名 | 默认 | 含义 |
---|---|---|
ve_mem_per_hash_table | 初始化时自动评估,8MB~256MB | hash表大小,join/aggr算子需要 |
ve_work_mem | 初始化时自动评估,4MB~128MB | sort/grouping sets算子需要 |
-
如果超过算子内存就会把多余的数据临时写入磁盘
-
一个sql可能同时有多个算子,比如有2个join,那么他就会最多申请2个ve_mem_per_hash_table的内存
-
如果多个sql并发执行,占用内存就会再翻倍
参数名 | 默认 | 含义 |
---|---|---|
ve_mem_two_level_hash_limit | 100MB | 大hash表阈值,一般设置50-500M |
对特别大的hash表,拆解成若干小份效率更高,超过ve_mem_two_level_hash_limit的就拆分
参数名 | 默认 | 含义 |
---|---|---|
ve_block_queue_mem | 512MB | 每两个算子间数据传递缓冲队列字节,一般不要超过默认值 |
ve_block_queue_capacity | 128 | 每两个算子间数据传递缓冲队列长度,最小16,一般不要超过默认值 |
-
sql执行时上下游算子之间每次传递一个block的数据,比如 scan 传递给join。
-
如果join没及时接收,就会把这个block缓冲下来存入缓冲队列
-
如果缓冲区满,scan算子就先停止工作,待join接收至少1个block后继续
-
队列长度或队列字节数有一个达到就认为队列满
-
一个sql存在多个算子,就可能存在多个队列,所以数值不宜设置过大
-
一般一个block大小在100k~10MB之间
参数名 | 默认 | 含义 |
---|---|---|
ve_mem_per_block | 1GB | join/集合算子 一个block大小限制,超过后主动切分block |
ve_rows_per_block | 32768 | join/集合算子 一个block行数限制,超过后主动切分block |
ve_force_split_large_block | off | 其他算子,block行数超过限制后,是否强制切分为小block |
-
join/集合 算子存在输出行数比输入行数多的情况,比如10行join20行,最差的结果可能有200行,
-
这可能导致block过大,即超过字节数限制或超过行数限制,此时join算子会在输出时主动切分
-
其他算子也可以设置强制切分,不包括 join/集合/motion
参数名 | 默认 | 含义 |
---|---|---|
ve_rows_per_motionsend | 1024 | motion算子 一个block行数限制,超过后主动切分block |
motion算子负责在集群的各个节点间传送书记,此参数与ve_rows_per_block类似,但不宜设置过大
参数名 | 默认 | 含义 |
---|---|---|
ve_thread_pool_max_threads | 256 | 最大运行线程数,所有sql总计不能同时运行的线程数,一般128-512 |
ve_thread_pool_queue_size | 512 | 最大等待线程数,大于最大运行线程数的线程会在队列排队,一般2-4倍的运行线程数 |
VE引擎日志¶
日志文件一般位于数据目录的sd_log目录下
参数log_min_messages
可以设置日志级别
debug5,debug4,debug3,debug2,debug1,debug,log,info,notice,warning,error
从左向右代码从详细到简单的过程,默认级别warning。当有问题调试时推荐debug1可以看出大部分信息。
从中可以看出的有效信息为:
- 一般信息 日期 [ 进程号 - 地址 ] 信息
2020-12-28 19:12:58.334 CST [23135 - 0x7f073ce45cc0] Trace: Aggregator.cpp:1460 : Merging aggregated data
- 实际执行的并行度
Scan(4) opened 8threads
代表当前scan操作使用了8并行
- join操作是全在hash表里还是部分落盘
如果你看类似下面的日志代表有部分数据落盘
HashJoin.cpp:77 : Spill total512.00 KiB, spilled 7 16.00 KiB
- 聚合操作是否使用两阶段聚合
Merge Single Level Data finished # 使用单一阶段聚合
Converting aggregation data to two-level # 使用两阶段聚合
VE引擎不支持的场景¶
在seabox数据库中VE引擎目前尚不支持部分场景,以下是对不支持场景的描述。
可通过explain (exec_mode)检查VE引擎是否支持
虽然VE引擎支持大部分场景,但仍有部分函数,语法等不支持,判断的方法为:
-
在sql的最后出现
Execution Mode: batch
即可被VE支持 -
如果出现
Execution Mode: row
表示不支持VE引擎
例1: VE引擎支持
# explain (exec_mode) select count(*) from t1 order by 1;
QUERY PLAN
-------------------------------------------------------------------------------
Sort (cost=123.65..123.65 rows=1 width=8 emode=batch->row)
Sort Key: (count(*))
-> Aggregate (cost=123.62..123.64 rows=1 width=8 emode=batch)
-> Seq Scan on t1 (cost=0.00..100.90 rows=9090 width=0 emode=batch)
Execution Mode: batch
例2: VE引擎不支持(行表达式)
# explain(exec_mode)update t1 set ( a, b) = (1,2);
QUERY PLAN
--------------------------------------------------------------
Update on t1 (cost=0.00..100.90 rows=9090 width=18)
-> Seq Scan on t1 (cost=0.00..100.90 rows=9090 width=18)
Execution Mode: row
(3 rows)
以下是VE不支持场景的说明:
VE不支持目标表为系统表¶
系统表主要包括以下:
pg_aggregate | pg_extension | pg_proc | g_stat_bgwriter |
pg_stat_user_tables | pg_am | pg_extprotocol | pg_publication |
pg_stat_database | pg_stat_wal_receiver | pg_amop | … |
seaboxsql=# explain (exec_mode) select * from pg_aggregate;
QUERY PLAN
---------------------------------------------------------------
Seq Scan on pg_aggregate (cost=0.00..3.53 rows=153 width=78)
Execution Mode: row
(2 rows)
可见,系统表的执行模式Execution Mode
均为row
。
查询中涉及 VE 不支持的函数¶
当查询语句中包含VE不支持的函数时,VE引擎不能执行该语句。通过排除VE的支持函数,来确定不支持的函数。
-
VE支持的函数:
-
查看VE支持的普通函数:
select ve_functions();
-
查看VE支持的聚集函数:
select ve_aggr_functions();
-
查看通用引擎支持的函数:
select proname, pg_proc.oid, prosrc,proargtypes, prorettype from pg_proc;
-
函数执行模式:
-
函数执行模式参数为:
ve_debug_function
-
函数的执行模式为:
'force_ve'
仅由VE引擎执行,不支持时报错;'force_pg'
由通用引擎执行;'hybrid'
由两种引擎混合执行,主要是调整函数类型、函数名称等对应关系,使其保持一致,VE可执行的由VE执行,否则再转为普通引擎执行。默认设置为ve_debug_function = hybrid
。注:如果设置强制VE执行:
set ve_debug_function = 'force_ve';
,VE不支持时会报错。
除此以外,还可以使用explain(exec_mdoe)
检查VE的函数执行模式。
create table t1(a int);
insert into t1 values(1);
seaboxsql=# explain(exec_mode) select to_char(a, '0d') from t1;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; executors: 3) (cost=0.00..1719.25 rows=96300 width=32 emode=batch->row)
-> Seq Scan on t1 (cost=0.00..435.25 rows=32100 width=32 emode=batch)
Optimizer: SeaboxSQL query optimizer
Execution Mode: batch
Hybrid Function: 1773
(5 rows)
当explain(exec_mode)
中出现Hybrid Function
关键字时,代表有函数通混合执行。
- 不支持特殊函数情况
不支持聚集函数中filter关键字 例如:select sum(c1) filter (where c1>1) from t1;
不支持的算子¶
ProjectSet | MergeAppend | RecursiveUnion |
Sequence | BitmapAnd | BitmapOr |
DynamicSeqScan | SampleScan | IndexScan |
DynamicIndexScan | IndexOnlyScan | BitmapIndexScan |
DynamicBitmapIndexScan | BitmapHeapScan | DynamicBitmapHeapScan |
TidScan | FunctionScan | ValuesScan |
TableFuncScan | TupleSplit | NamedTuplestoreScan |
WorkTableScan | CustomScan | RowTrigger |
AssertOp | Gather | GatherMerge |
PartitionSelector | LockRows | TableFunctionScan |
ShareInputScan | Repeat | SplitUpdate |
VE引擎不支持的表达式¶
以下表达式VE引擎不支持:
Expr/Func |
---|
FieldSelect |
DistinctExpr |
ArrayExpr |
RowExpr |
RowCompareExpr |
XmlExpr |
CoerceToDomain |
不支持domain自定义数据类型¶
create domain表示创建自定义数据类型。VE不支持自定义类型,用例:
create domain vc4 as varchar(4);
create table vc4table (f1 vc4);
insert into vc4table values('111');
seaboxsql=# select * from vc4table;
ERROR: VE_CHECK: disable VE: type: 41010 not supported.
不支持带参数nest loop join
¶
用例:
create table int8_tbl ( q2 int8);
insert into int8_tbl values(3);
create function extractq2(t int8_tbl) returns int8 as $$ select t.q2 $$ language sql immutable;
seaboxsql=# select x from int8_tbl, extractq2(int8_tbl) f(x);
ERROR: VE_CHECK: disable VE: NestLoop(with NestParam) is not supported by VE.
不支持INSERT/DELETE/UPDATE ... RETURNING ...
¶
用例:
create table int8_tbl ( q2 int8) using scolumn;
insert into int8_tbl values(3);
seaboxsql=# explain(exec_mode) update int8_tbl set q2 = 1 returning *;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; executors: 3) (cost=0.00..3765.00 rows=172200 width=18)
-> Update on int8_tbl (cost=0.00..1469.00 rows=57400 width=18)
-> Explicit Redistribute Motion 3:3 (slice2; executors: 3) (cost=0.00..1469.00 rows=57400 width=18)
-> Split (cost=0.00..321.00 rows=57400 width=18)
-> Seq Scan on int8_tbl (cost=0.00..321.00 rows=28700 width=18)
Optimizer: SeaboxSQL query optimizer
Execution Mode: row
(7 rows)
seaboxsql=# explain(exec_mode) delete from int8_tbl returning *;
QUERY PLAN
-------------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; executors: 3) (cost=0.00..1469.00 rows=86100 width=10)
-> Delete on int8_tbl (cost=0.00..321.00 rows=28700 width=10)
-> Seq Scan on int8_tbl (cost=0.00..321.00 rows=28700 width=10)
Optimizer: SeaboxSQL query optimizer
Execution Mode: row
(5 rows)
seaboxsql=# explain(exec_mode) insert into int8_tbl select * from int8_tbl returning *;
QUERY PLAN
-------------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; executors: 3) (cost=0.00..1469.00 rows=86100 width=8)
-> Insert on int8_tbl (cost=0.00..321.00 rows=28700 width=8)
-> Seq Scan on int8_tbl int8_tbl_1 (cost=0.00..321.00 rows=28700 width=8)
Optimizer: SeaboxSQL query optimizer
Execution Mode: row
(5 rows)
查询中涉及VE不支持的数据类型¶
当查询语句中包含不支持数据类型的列时,VE引擎不能执行该语句。VE不支持的数据类型如下表所示:
smallserial | serial | bigserial | money |
name | point | line | lseg |
box | path | path | circle |
cidr | macaddr | macaddr8 | bit(n) |
bit varying(n) | tsvector | tsquery | xml |
json | jsonB | int4 range | int8 range |
numrange | tsrange | tstzrange | daterange |
例
create table t1 ( a name);
explain (exec_mode) select * from t1;
QUERY PLAN
-------------------------------------------------------
Seq Scan on t1 (cost=0.00..45.50 rows=3550 width=64)
Execution Mode: row
(2 rows)
VE引擎对Decimal类型的限制¶
-
最大进度范围是(38,2),而不是1000位
-
不指定精度时默认使用(18,2),而不是自适应精度
修改默认精度,可以通过修改参数完成
名称 | 默认 |
---|---|
default_decimal_precision | 18 |
default_decimal_scale | 2 |
-
不支持NaN数值
-
隐式类型转换
对未指定精度和标度的decimal类型进行类型转换时,VE引擎会进行评估precision
和 scale
的数值,整型会转换为固定精度的decimal类型,其他类型会先转换成decimal32/64/128,再评估出具体的精度和标度,如下表所示:
数值类型 | Decimal类型 |
---|---|
UInt8 | decimal(3,0) |
UInt16 | decimal(5,0) |
UInt32 | decimal(10,0) |
Int64 | decimal(19,0) |
UInt64 | decimal(18,0) |
Decimal32 | decimal(9,0) |
Decimal64 | decimal(18,0) |
Decimal128 | decimal(38,0) |
float | decimal(18,6) |
- 计算结果保留小数点位数与普通引擎不同
create table t1 ( a numeric);
insert into t1 values(1),(1),(0);
set ve_enable =1;
select avg(a) from t1;
avg
----------
0.666666
(1 row)
set ve_enable =0;
select avg(a) from t1;
avg
-----------------------
0.66666666666666666667
(1 row)
- 计算过程判断结果溢出条件不同
由于实现机制不同,普通引擎只要最终结果不溢出就不会溢出,VE引擎在计算过程的溢出也会导致最后溢出,简单地说,VE引擎更容易触发数据越界
create table t1 ( a numeric(10,0));
insert into t1 values(1234567890);
set ve_enable =0;
select a* a *a*a*a/a from t1;
?column?
---------------------------------------
2323057227982592441500937982514410000
(1 row)
set ve_enable =1;
select a* a *a*a*a/a from t1;
ERROR: Decimal math overflow
日期时间类型支持的限制¶
VE引擎日期时间类型的数据范围与原生行执行引擎不同,下表是具体差异:
数据类型 | 普通引擎范围 | VE引擎范围 |
---|---|---|
timestamp | 4714/11/24 BC~294276/12/31 | 4713/1/1 BC~9999/12/31 |
timestamp with time zone | 4714/11/24 BC~294276/12/31 | 4713/1/1 BC~9999/12/31 |
date | 4714/11/24 BC~5874897/12/31 | 4713/1/1 BC~9999/12/31 |
time | 00:00:00~24:00:00 | 00:00:00~24:00:00 |
time with time zone | 00:00:00+1459~24:00:00-1459 | 不支持 |
interval | -178000000年~178000000年 | 不支持 |