跳转至

矢量计算执行器——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引擎会进行评估precisionscale 的数值,整型会转换为固定精度的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年 不支持