PG 向量化引擎--1

Posted yzs的专栏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PG 向量化引擎--1相关的知识,希望对你有一定的参考价值。


PG 向量化引擎

向量化引擎是OLAP数据库提升性能的有效技术。翻到PostgreSQL邮件列表有对向量化引擎的讨论。这里进行整理,以作分析。

作者邮件

代码位于https://github.com/zhangh43/vectorize_engine ,并且合入了PG13中。其基本思想是扩展TupleTableSlot,引入VectorTupleTableSlot(一个由投影列组织的列数组)。每列的数组在内存中连续。这使得表达式计算时能够很好使用缓存,并且可以使用SIMD。我们已经重构了SeqScanNode和AggNode,目前支持VectorTupleTableSlot。

下面时我们设计的特点:

1)纯扩展。不会将任何代码解码到PG内核中

2)CustomScan节点。我们使用CustomScan框架来替换原有的执行器节点,如SeqScan、Agg等。基于CustomScan,我们可以扩展CustomScanState、BeginCustomScan()、ExecCustomScan()、EndCustomScan接口来实现向量化执行逻辑。

3)Post planner hook。产生Plan后,使用plan_tree_walker来遍历执行计划树,检测是否可以向量化。如果可以,那么使用向量化节点(以CustomScan节点的形式)替换非向量化节点(如SeqScan、Agg等)。如果不可以,重新转换到原始执行计划,并使用非向量化执行器。未来会改进这一部分,例如当一些节点不能向量化时不再转换到原始执行计划,而是使用Batch/UnBatch节点来产生一个向量化和非向量化节点来兼容。

4)支持逐步实现一个新的向量化执行节点。当前仅支持向量化SeqScan和Agg,但是开启向量化插件后,其他包括Join的查询也可以执行。

5)继承原始执行器代码。我们选择了一个更加平滑的方式更改当前PG执行器节点并将之向量化,而不是重新写整个执行器。拷贝了当前执行器node的c文件到我们的扩展中,基于此添加了向量化逻辑。当PG改进执行器时,我们可以很方便地将之合入我们插件。我们想了解,通过扩展来实现向量化执行器是否是个好方法?

6)可拔插存储。PG现在已支持可拔插存储了。TupleTableSlot被重构抽象为TupleTableSlotOps结构。当我们将PG升级到最新版本时,VectorTupleTableslot可以基于此框架完成升级。

我们执行TPCH(10G)benchmark,Q1的结果对比:PG是50s,向量化PG是28s。通过以下方法性能可以得到提升:

1)heap tuple的解码占用更多CPU资源。未来我们会使用zedstore,向量化执行器更适合列存。

2)向量化agg并未完全向量化。我们还需要做很多优化。例如,批量计算hash值,优化x向量化HashAgg的hash表

3)将Datum转换成真实类型的代价以及反操作的代价都很高,例如DatumGetFloat4 & Float4GetDatum。一个优化方法是在VectorTupleSlot中直接存储真实类型,而不是datums的数组。

相关工作:

1)VOPS也是一个向量化执行插件:https://github.com/postgrespro/vops.

2)Citus向量化执行器:https://github.com/citusdata/postgres_vectorization_test。它使用ExecutorRun_hook来运行向量化执行器,使用cstorefdw来支持列存储。

注意,现在向量化执行器基于PG9.6,但是可以通过一些努力移植到master/zedstore。在朝着这个方向前进时,希望收到反馈,我们不胜感激。

Postgres Professional的Konstantin Knizhnik反馈及作者答复

我认为向量化执行器对PG来说是绝对必要的,特别是考虑下到现在我们由列存原型zedstore。为了充分利用列存带来的优势,我们绝对需要一个向量化执行器。

但是,我不完全理解为什么建议将其作为扩展来实现。是的。自定义节点可以在不影响PG内核情况下提供向量化执行。但是为了高效继承zedstore和向量化执行器,我们需要扩展table-AM(VectorTupleTableSlot和对应扫描函数)。当然将向量化执行器作为扩展更加容易,但我认为迟早应该将它添加到PG内核中。

据我了解,您已经由了一些原型实现(否则您是如何获得性能结果的?)如果是这样,您是否打算发布它,或者您认为应该从头开始开发执行器?

答复

原型扩展位于https://github.com/zhangh43/vectorize_engine。同意某一天将向量化执行器添加到PG内核中。但是这么大的特性,不仅需要改变table-AM,还需要改变每个执行器节点,例如Agg,Join,Sort节点等。以及表达式计算函数和聚合的transition函数、combine函数等。我们也需要将之向量化。因此第一步作为一个插件来完成,如果在社区中流行并且稳定下来,我们随时可以合入PG内核中。

我们确实希望从社区得到一些关于CustomScan的反馈。CustomScan只是一个抽象层。通常用于支持用户定义的扫描节点。但其他一些PG扩展(pgstorm)已经将之用作通用的CustomNode,例如Agg,Join等。由于向量化引擎需要在所有节点中支持向量化处理,因此遵循上述思路,我们选择使用CustomScan。

基于VOPS经验的一些担忧:

1)对于某些类型的查询,向量化模型(列式)性能具有优势,但是对于其他某些类型的查询,他的效率较低。此外,数据以行形式导入数据库。一行一行插入列存非常低效。因此需要某些批量导入工具,可以在导入列存之前缓冲插入的数据。实际上这是数据模型的问题,而不是向量化执行器的问题。但我想在这里表达的是,最好同时拥有2中表示(水平和垂直)并让优化器为特定查询选择最有效的一种

答复

是的,一般来说对于OLTP查询,行格式更好,而对于OLAP查询,列存更好。至于存储类型 (或数据模型),我认为DBA应该选择行存储或列存储以用于特定表。至于执行器,让优化器根据成本来进行选择是一个好主意。这是一个长期目标,我们的扩展现在需要返回到原始行执行器来进行Insert、update、indexScan。我们希望我们的扩展可以逐步增强。

2)列存和向量化执行器对于select sum(x) from T where...之类的查询最有效。不幸的是,这种简单的查询在现实生活中很少使用。通常分析查询包含group by和joint。而且这里的向量模型并不总是最优的(你必须从列中重建行来执行join和分组)。为了提高查询执行效率,可能需要为同一数据创建多个不同投影(按属性的不同子集排序)。

这就是为什么Vertica支持投影的原因。在VOPS中也可以这么做:使用create_projection按时,可以执行哪些属性应该是标量,哪些可以向量化。在这种情况下,可以使用标准的PG执行器执行分组和join,同时执行向量化操作以过滤和持续聚集。

这就是为什么Q1在VOPS中快20倍,而不是原型中的2倍。所以我认为列存应该可以维护表的多个投影,优化器应该能够为特定查询自动选择其中一个。投影的同步肯定是一个挑战问题,幸运的是,OLAP通常不需要最新数据。

答复

Vertica中投影很有用,我测试过,VOPS确实很快。如果你能够将之贡献给PG内核,那就太好了。我们的扩展旨在不更改任何PG内核代码、用户SQL和现有表。我们将继续优化我们的向量化实现:向量化hashagg需要实现向量化hash表、批量计算hash key、批量探测hash表等。当然PG中的原始hash表不是向量化hash表。

3)我想知道向量化的执行器是否应该只支持内置类型和预定义的运算符?或者它应该能够与任何用户定义的类型、运算符、聚合一起使用?当然,支持内置标量类型要容易的多,但这与PG的开放性和可扩展性相矛盾。

答复

是的,我们应该支持用户定义的类型。这可以通过引入将行类型映射到向量类型的寄存器层来完成。例如int4->vint4

4)你有没有想过VectorTupleTableSlot中存储数据的格式?它应该是基准数组吗?或者我们需要以更底层格式表示向量(例如对于rel4类型的float数组)

答复:

我们测试结果显示dataum转换不高效,我们准备使用你提到的底层数组格式来实现datum数组。

原文

​https://postgrespro.com/list/id/CAB0yrem3PYu2qQD4=JOg8y_5QAK+Q+K5yEptxs0t71j5cRyoOQ@mail.gmail.com​

以上是关于PG 向量化引擎--1的主要内容,如果未能解决你的问题,请参考以下文章

NumPy基础-数组与向量化计算

如何用 AVX2 向量化 a[i] = a[i-1] +c

SPARK的计算向量化-spark本身的向量化

TiDB:向量化执行使表达式性能提升10倍成为可能

SPARK的计算向量化-已有的向量化项目

矢量化