• [技术干货] 向量化的演进
    在第一代向量化引擎之后,GaussDB(DWS)演化出具有更高性能的向量化引擎:Sonic 向量化引擎和 Turbo 向量化引擎。GaussDB(DWS)为了 OLAP 执行性能提升,在列存+向量化执行引擎、批量计算的路上不断演进:Stream 算子+分布式执行框架,支持数据在多节点间流动;SMP,节点内多线程并行,充分利用空闲硬件资源;LLVM 技术,全新的代码生成框架,JIT(just in time)编译器,消除 tuple deform瓶颈;Sonic 向量化引擎,对 HashAgg、HashJoin 算子进一步向量化,根据每列不同类型实现不同 Array 来对数据做计算;新一代 Turbo 向量化引擎,对大部分算子做进一步向量化,在 Sonic 引擎的基础上,新增了 Null 优化、大整数优化、Stream 优化、Sort 优化等,进一步提升了性能。物化算子是一类可缓存元组的节点。在执行过程中,很多扩展的物理操作符需要首先获取所有的元组才能进行操作(例如聚集函数操作、没有索引辅助的排序等),这是要用物化算子将元组缓存起来。连接算子对应了关系代数中的连接操作,以表 t1 join t2 为例,主要的集中连接类型如下:inner join、left join、right join、full join、semi join、 anti join,其实现方式包括 Nestloop、HashJoin、MergeJoin。 扫描算子用来扫描表中的数据,每次获取一条元组作为上层节点的输入, 存在于查询计划树的叶子节点,它不仅可以扫描表,还可以扫描函数的结果集、链表结构、子查询结果集。
  • [技术干货] 向量化执行引擎的性能
    对比行列存引擎对同一表达式x*(1-y)计算的性能,可以看到列存引擎的Cstore Scan算子相比行存引擎的 Seq Scan 算子,耗时减少了 85%。向量计算的特点是:一次计算多个值,减少函数调用和上下文切换,尽量利用 CPU 的缓存以及向量化执行指令提高性能。向量化执行引擎的性能优势:一次一 Batch,读取更多数据,减少 IO 读次数;由于 Batch 中记录数多,相应的 CPU 的 cache 命中率提升;Pipeline 模式执行过程中的函数调用次数减少;与列存表配套,减少 tuple deform,即列存数据重构 tuple 的时间开销。向量化引擎的执行算子类似于行执行引擎,包含控制算子、扫描算子、物化算子和连接算子。同样会使用节点表示,继承于行执行节点,执行流程采用递归方式。主要包含的节点有:CStoreScan(顺序扫描),CStoreIndexScan(索引扫描),CStoreIndexHeapScan(利用 Bitmap 获取元组),VecMaterial(物化),VecSort(排序),VecHashJoin(向量化哈希连接)等,下面将逐一介绍这些执行算子。扫描算子用来扫描表中的数据,每次获取一条元组作为上层节点的输入, 存在于查询计划树的叶子节点,它不仅可以扫描表,还可以扫描函数的结果集、链表结构、子查询结果集。
  • [技术干货] 执行框架
    执行器是优化器与存储引擎的交互枢纽。以优化器生成的执行计划树为输入,从存储引擎访问数据,并按照计划,操作各种执行算子,从而实现数据的处理。采用 Pipeline 模式, 行执行器一次一 tuple,列执行器一次一 batch。上层驱动下层,使得数据在执行树上流动。提供各种数据处理的执行算子。下图展示了自上而下的控制流和自下而上的数据流。执行器的执行过程可分为这三个步骤:1) 执行器初始化:构造执行器全局状态信息 estate、递归遍历计划树各节点,初始化其执行状态信息 planstate 2) 执行器的执行:行引擎和向量化引擎入口独立开,从计划树根节点开始,递归遍历到叶节点获取一个 tuple/batch,经过逐层节点算子的处理,返回一个结果tuple/batch,直到再无 tuple/batch。3) 执行器的清理:回收执行器全局状态信息,清理各 plan node 的执行状态。行执行器的问题是:CPU 大部分处理在遍历 Plan Tree 过程,而不是真正处理数据,CPU 有效利用率低。列存表独有的应用场景,需要配套的向量化引擎,才能真正发挥其在OLAP 场景下提升性能的优势。因此,列执行器的改造基本思路为:一次处理一列数据。和行执行器一样,向量化执行引擎调度器,遵循 Pipeline 模式,但每次处理及在算子间传递数据为一次一个 Batch(即 1000 行数据),CPU 命中率提高,IO 读操作减少。
  • [技术干货] 向量化执行引擎详解
    GaussDB(DWS)包含三大引擎,一是 SQL 执行引擎,用来解析用户输入的 SQL 语句,生成执行计划,供执行引擎来执行;二是执行引擎,其中包含了行执行引擎和列执行引擎,执行引擎即查询的执行者,位于优化器和存储引擎之间,负责将数据从存储引擎中读取出来,并根据计划将数据处理加工后返回给客户端,执行引擎的目标是为了更好地利用计算资源,更快地完成计算。三是存储引擎,决定了数据库数据的存取方式,直接影响了数据库的读写性能。其中行执行引擎应用于行存表中,传统的 OLTP(OnLine Transaction Processsing 联机事务处理)场景与功能、业务强相关,数据需要进行频繁的增删改查,这时比较适合使用行存储式。行存储的优势主要有两个方面:首先是点查性能好,在点查场景下可以直接索引到某行数据的元组位置;其次就是更新效率高,行存储在实时并发入库,并发更新方面依然有着比较大的优势。行执行引擎的关键就是:一次处理一行数据,即一 tuple,适合数据频繁更新,增删改操作多,且查询结果涉及表的多列的场景。传统的行执行引擎大多采用一次一元组的执行模式,这样在执行过程中 CPU 大部分时间并没有用来处理数据,更多的是在遍历执行树,就会导致 CPU 的有效利用率较低。而在面 对 OLAP 场景巨量的函数调用次数,需要巨大的开销。为了解决这一问题,GaussDB(DWS)中增加了向量化引擎。向量化引擎使用了一次一批元组的执行模式,能够大大减少遍历执行节点的开销。同时向量化引擎还天然对接列存储,能够较为方便地在底层扫描节点装填向量化的列数据。列存+向量化执行引擎,是打开 OLAP 性能之门的金钥匙之一。
  • [技术干货] Stream 算子相关的节点
    Gather Stream,用于 CN 收集 DN 的结果;Redistribute Stream,用于将 DN 上的数据重分布给其他 DN 做 HashAgg。Q1 查询是一个单表查询,为什么需要在 DN 之间重分布数据呢?tpch 的 Q1 是对 lineitem 表的单表查询,进行分组聚集计算。因为 lineitem 表是按照 l_orderkey 列作为分布列,将数据分布到各 DN 的。而聚集操作分组键是l_returnflag 和 l_linestatus,而不是分布键 l_orderkey,所以,属于同一个分组的数据分布在不同的 DN 上,这就需要各 DN 重新按照分组键作为分布键,将数据分发给其他 DN,使得各 DN 上拥有相同分组键的数据完成聚集操作。从以上的计划中,可以看到在 DN 上先做了 hashagg,再又重分发给其他 DN,再次做 hashagg,如下图中的右侧。为什么不是先把数据重分布发给其他 DN 后,做一次 hashagg 即可。这两个计划的区别在于:如果 scan 后数据量非常大,而聚集后数据量比较小的情况,就适合用第二个计划,可以减少 DN 之间的数据流动,降低网络开销。从上面 Q1 的执行计划中,可以明显看到,Seq Scan on lineitem 对应的 E-rows 为 6001622,即 seqscan后的数据估计有 6001622 行,重分布之前 HashAggregate 的的结果数据估计只有 18行,数据量大大减少。根据代价估算,重分布前做一次 hashagg 的代价,比直接重分布scan 的结果的代价要小很多,故选择了重分布前先做一次 hashagg。在这个计划中我们看到对 region 表的扫描结果使用 Broadcast 方式广播给其他 DN节点,然后在各 DN 上和 nation 表做 Nestloop 连接。
  • [技术干货] Shared nothing的分布式架构
    CN(Coordinator Node)为对外服务和协调节点,负责接收客户端连接以及对用户 SQL 命令解析下发,并收集 DN 节点执行的结果进行汇总,将结果展现给客户端;DN(Datanode)为内部数据节点,承载内部数据存储及计算单元的功能;GTM(Global Transaction Manager)负责集群全局事务控制,其与 Datanode均有本地主备双机功能。对应于分布式架构, GaussDB(DWS) 提供了分布式执行框架,这也是GaussDB(DWS)中最核心的技术,旨在充分利用 DN 的资源,尽量将计算下推到 DN 进行,避免 CN 成为瓶颈,以提升查询效率和系统扩展性。分布式执行框架的技术特点如下:CN 负责查询请求的解析、基于代价进行优化以及向 DN 进行任务下发,并收集DN 节点执行的结果进行汇总;DN 上运行执行计划进程,基于本节点存储的数据执行任务;执行过程中每个算子都是接收下级算子的数据输入,并向上级算子输出数据。是一个生产者—消费者的流水线工作模型。首先从 id 为 6 的行开始,对 lineitem 表执行 seqscan;id 为 5 的行,对下层扫描得到的数据根据 GROUP BY 分组键执行hashaggregate 操作;id 为 4 的行,是一个 Redistribute Stream 算子,它将在 DN 上的 local 数据执行 agg 后结果,重分布给其他 DN;id 为 3 的行,各 DN 收到其他 DN 上的数据,重新做 agg;id 为 2 的行,对下层的 agg 结果进行排序;id 为 1 的行,是一个 Gather Stream 算子,CN 节点收到 DN 返回的结果,汇集后将最终结果展示给客户端
  • HCS8.3.1搭建Gauss DB24.1.30版本自动备份失败
    HCS8.3.1搭建Gauss DB24.1.30版本自动备份至OBS3.0对象存储失败,数据库备份的网络策略如何放通?需要放通的端口是什么?
  • [技术干货] LLVM 适用场景
    目前 LLVM 仅支持 DN 上且是列存向量化执行路径的查询作业,其支持的表达式及算子如下:支持 LLVM 的表达式: Case…when… 表达式;In 表达式;Bool 表达式 (And/Or/Not);BooleanTest 表达式(IS_NOT_KNOWN/IS_UNKNOWN/IS_TRUE/IS_NOT_TRUE/IS_FALSE/IS_NOT_FALSE);NullTest 表达式 (IS_NOT_NULL/IS_NULL);Operator 表达式;Function 表达式 (lpad, substring, btrim, rtrim, length);Nullif 表达式。表达式计算支持的数据类型包括 bool, tinyint, smallint, int, bigint, float4, float8, numeric, date, time, timetz, timestamp, timestamptz, interval, bpchar, varchar, text, oid。仅当表达式出现在向量化执行引擎中 Scan 节点的 filter、Hash Join 节点中的complicate hash condition、hash join filter、hash join target, Nested Loop 节点中的 filter、join filter, Merge Join 节点的 merge join filter, merge join target, Group节点中的 filter 表达式时,才会考虑是否使用 LLVM 动态编译优化。支持 LLVM 的算子: Join :HashJoin Agg :HashAggSort 其中 HashJoin 算子仅支持 Hash Inner Join,对应的 hash cond 仅支持 int4、bigint、bpchar 类型的比较;HashAgg 算子仅支持针对 bigint、numeric 类型的 sum及 avg 操作,且 group by 语句仅支持 int4、bigint、bpchar,text,varchar,timestamp类型操作,同时支持 count(*)聚集操作。Sort 算子仅支持对 int4,bigint,numeric,bpchar,text,varchar 数据类型的比较操作。除此之外,无法使用 LLVM 动态编译优化,具体可通过 explain performance 工具进行显示。
  • [技术干货] 如何使用 LLVM
    在 GaussDB(DWS)中,涉及 LLVM 的 GUC 参数有两个:1) enable_codegen:总开关,用于控制是否开启 codegen,默认为 on;2) codegen_cost_threshold:使用处理行数控制是否开启 codegen,默认门槛值为 10000。目前 GaussDB(DWS)中并非是通过计划代价去控制是否开启 codegen,而是通过处理行数来控制的。此处 10000 是通过实验验证得出的优化值,不建议将此门槛值设置的过低,因为代码执行过程中的即时编译是有代价的。此处仅截取部分执行信息,在 Datanode Information 中的扫描算子中有 LLVM Optimized 信息,代表已经使用了 JIT 编译。如果想查看 LLVM JIT 编译的时间耗时,可以借助 GUC 参数 analysis_options 进行设置后,执行对应查询语句,在 User Define Profiling 中就可以看到 LLVM 的编译时间。其中 LLVM Compilation 即为 LLVM 的即时编译的时间代价。此处编译的时间代价通常会与 SQL 执行流程的复杂程度成正比关系,在实际的调优实践中可以结合此数据对处理数据行数的门槛值做进一步的调整。
  • [技术干货] LLVM 技术解析
    LLVM 这个名字最早源于底层虚拟机(Low Level VirTual Machine)的首字母缩写,但随着 LLVM 项目的演进,底层虚拟机的含义已经不再适用于 LLVM。现在谈到 LLVM,广义上讲就是指 LLVM 本身,它是一套用于开发编译前端与后端的工具套件,狭义上讲LLVM 就是指整个编译套件的优化器及后端,而 CLANG 可以认为是 C/C++的前端。传统编译器最常见的三阶段设计:前端:解析源代码生成抽象语法树;优化器:根据优化规则对代码进行改善,相当于规则重写,例如消除冗余计算等;后端:将代码映射到目标指令集上,包括指令选择、寄存器分配和指令调度等。GCC 是一个完整的可执行文件,没有为其他语言的开发者提供代码重用的接口,灵活性不足。LLVM 也采用经典的三段式设计,但与传统编译器最大的不同就是针对不同语言都提供了同样一种中间表示 IR 以及模块化的后端(MCJIT 模块可以支持 JIT 编译)。为具体的查询生成定制化的机器码代替通用的函数实现,并尽可能的将数据存储在CPU 的寄存器中:1) 解决条件逻辑冗余的问题,需要用到 JIT(jiust-in-time)编译技术,LLVM 天然支持 JIT 技术;2) 减少大量虚函数调用;3) 改善数据调用,将数据尽可能的从内存加载到 cache 上;4) 发挥通用硬件平台的扩展指令集功能,例如 SSE4.2。
  • [技术干货] 自动收集场景
    需要进行自动统计信息收集的场景通常有五个:批量 DML 结束时,增量 DML 结束时,DDL 结束时,查询开始时和后台定时任务。autovacuum_analyze_threshold #表触发 analyze 的最小修改量。autovacuum_analyze_scale_factor #表触发 analyze 时的修改百分比。当"表自上次 Analyze 以来修改的条数" >= autovacuum_analyze_threshold + 表估算大小 * autovacuum_analyze_scale_factor 时,需要自动触发 analyze。不同表的数据特征不一样,需要触发 Analyze 的阈值可能有不同的需求。表级阈值优先级高于全局阈值。当查询中存在“统计信息完全缺失”或“修改量达到 Analyze 阈值”的表,且执行计划不采取 FQS (Fast Query Shipping)执行时,则通过 autoanalyze 控制此场景下表统计信息的自动收集。此时,查询语句会等待统计信息收集成功后,生成更优的执行计划,再执行原查询语句。当 autovacuum 设置为 on 时,系统会定时启动 autovacuum 线程,对“修改量达到 Analyze 阈值”的表在后台自动进行统计信息收集。当一个表的 distinct 总是估算不准,例如:数据扎堆儿重复场景。如果表的 distinct 值固定,可以通过以下方式冻结表的 distinct 值。
  • [技术干货] 采样方法
    我们支持了两种采样模式,分别适用于不同样本量的场景。1) 固定值采样 采集样本放在内存,通过内置算法函数计算统计信息。适用于较小样本量时;2) 百分比采样 采集样本放入临时表,通过 SQL 计算统计信息。适用于按百分比指定采样大小,和多列统计信息收集。如果计算的采样样本数大于等于总数据量的 2%,且用户表的数据量小于 1600000时,ANALYZE 所需时间相比 guc 参数为默认值的时间会有所增加。采样率为正数,表示使用固定值采样。采样率为负数,表示使用百分比采样。目前默认收集统计信息的采样大小是 30000 行(default_statistics_target*300),如果表的统计信息估算不准,可以尝试增大采样率。通过单独设置列属性 attstattarget,可以设置表级的采样大小。表采样大小取所有列的最大值。ALTER TABLE table_name ALTER [ COLUMN ] column_name SET STATISTICS [PERCENT] integer;为随后的 ANALYZE 操作设置针对每个字段的统计收集目标。目标的范围可以在 0 到10000 之内设置。设置为-1 时表示重新恢复到使用系统缺省的统计目标。为了避免解压太多 CU,我们按采样率计算里需要采集的 CU 数量,并且样本都集中落在这些随机选择的 CU 中。有时样本数不少但都比较集中可能导致批量重复场景的数据特征抓不准,这是可以通过cstore_cu_sample_ratio 参数,增大 CU 的选择倍数。
  • [技术干货] 统计信息分类
    统计信息分为表级统计信息和列级统计信息。表级统计信息存储在 pg_class 中,主要涉及 relpages 和 reltuples。列级统计信息 PG_STATISTIC 中,可以通过 pg_stats 视图查看。ID 类型 名称 说明 1 表级 relpages 表占用的物理页面数2 表级 reltuples 表中的记录数3 列级 nullfrac NULL 值占比4 列级 width 列的平均宽度,主要用于文本类型5 列级 distinct 唯一值的个数或占比6 列级 MCV高频值,一列中频繁出现的值的数组(按频率排序),同时生成一个一一对应的频率数组。7 列级 HISTOGRAM直方图,用等频直方图描述一列中数据的分布情况,高频值不会出现在直方图中。8 列级 CORRELATION相关系数,列的物理顺序和排序后逻辑顺序的相关系数。统计值范围从-1 到 1, 趋向于-1 表示逆向相关, 趋向于 1 表示正向相关, 趋向于 0 表示不相关。通常用来估算索引扫描的代价,相关系数越高, 索引扫描时离散块扫描更少, 代价也就越低。表级统计信息影响表大小的估算;列级统计信息影响查询表达式代价的估算。distinct 影响 JOIN 时内外表选择,width 影响内存大小估算,MCV 影响等值表达式,HISTOGRAM 影响范围表达式。
  • [技术干货] Analyze 使用功能
    Analyze 用于收集表的统计信息,新版本的 Analyze 在底层算法和性能上都有了较大改进:1) 将列存行数估算方法从“采集样本估算”改为“直接从 CUDesc 精准读取”,样本分配更加合理;2) 百分比 Analyze 性能优化,行存提升 44%,列存提升 73%;3) 全新的列存采样算法,随机性更好,性能提升 30%;4) 行存“蓄水池采样算法”随机性增强,统计信息更准;5) 将采样算法与存储格式分离;6) 解决删除元组多时,统计信息估算不准的问题;7) 解决小 CU 多时,统计信息不准的问题;8) 深度重构代码逻辑,增强代码可读性,健壮性。通过 7 处性能优化,4 处算法改进,9 次重点重构,在执行性能和统计信息准确性方面有了全面的提升。此外,Analyze 执行的是否及时,在一定程度上直接决定了 SQL 执行的快慢。因此,GaussDB(DWS)引入了自动统计信息收集,可以做到让用户不再担心统计信息是否过期。一条 SQL 语句的执行会经过如下过程:1) 查询解析:SQL 文本--(“词法分析和语义分析”)-->“原始语法树(Raw Abstract Syntax Tree)”;2) 查询分析:原始语法树--(查询分析和重写)-->查询语法树;3) 查询优化:查询语法树--(优化器逻辑优化和物理优化)-->执行计划;4) 查询执行:执行计划--(执行器执行)-->执行结果。查询的分析和重写,主要进行表的合法性检查,视图的展开等。优化器的逻辑优化,主要进行一些等价的代数变换,例如:关系表达式推导,子查询上拉,约束条件下推,约束条件合并,无效条件的移除等待。
  • [技术干货] 复杂一点的范围比较
    在 scalarineqsel 函数中只利用到了直方图,是因为unique 列没有重复值,也就不存在 MCV。下面我们用一个不是 unique 的普通列再看一下范围比较。这个范围包括两部分,重复次数比较多的值(在 MCV 中) 和 重复次数比较少的值(覆盖在直方图里),又由于计算直方图时去掉了 MCV 的值,因此 MCV 和直方图互相独立可以联合使用。小于 IAAAAA 的值在 MCV 中有前 6 个,因此把它们的 frac 累加起来,就是小于IAAAAA 且重复次数较多的人的概率。还有一部分小于 IAAAAA 但重复次数较少的人的概率 可以通过直方图进行范围计算。前面使用 unique1 列进行等值比较时,因为 unique 约束列不存在 MCV,只有直方图。因此,只计算在直方图中桶的覆盖占比就是选择率了。这里还要考虑落在 直方图中值的整体占比 histogram_fraction = 1 - sum(mcv_frac),直方图桶的覆盖占比 * 整个直方图整体占比就是在直方图中的选择率了。在这个特殊的例子中,从 MCV 中计算出来的选择率为 0.01833333,远小于从直方图中计算出来的选择率 0.28933593,是因为该列中数值分布的太平缓了(统计信息显示 MCV 中的这些值出现的频率比其他值要高,这可能是因为采样导致的错误)。在大多数存在明显重复值多的场景下,从 MCV 中计算出的选择率会比较明显,因为重复值的出现概率是比较准确的。 
总条数:1539 到第
上滑加载中