PG 向量化引擎--2
向量化引擎是OLAP数据库提升性能的有效技术。翻到PostgreSQL邮件列表有对向量化引擎的讨论。这里继续进行整理,以作分析。
关于设计中的几个问题
1、在vtype中使用原生数组而不是Datum数组会更有效吗?我认为针对float4和int32类型的操作,它将允许编译器产生更加有效的代码
是的,我也在考虑扫描列存时,将列batch加载到连续的内存区域中。对于Int32,此区域大小时4*BATCHSIZE,而对于Int16,大小时2*BATCHSIZE。所以使用原生数据可以只做一个memcpy来填充vtype的batch。
2、为什么VectorTupleSlot中包含元组的数据(batch)而不是向量(vtype的数组)?
首先,VectorTupleSlot在tts_values域存储vtype的数组,这样做减少了代码的更改量,摈弃可以重用像ExecProject类似的函数。当然,我们也可以使用单独的字段来存储vtypes
其次,VectorTupleSlot还包含堆元组数据。这属于堆元组的变形。事实上,一个batch中包含的元组可能跨多个页。因此我们需要pin住相关页的数组,而不仅仅是一个页
3、为什么必须实现子集的plan_tree_mutator而不是使用expression_tree_mutator?
我也想要替换Plan节点,例如Agg->CustomScan(使用VectorAgg实现)。expression_tree_mutator不能够用于变异plan node,如Agg,对吗?
4、据我了解,您现在总是尝试用自己定义的向量化scan来替代SeqScan。但只有当此扫描或聚合执行了quals才有意义。其他情况下,batch unbatch只会增加额外的开销,不是吗?
可能heap格式和select i from t没有qual、projection、aggregation的查询才会有额外的开销。但是对于列存,VectorScan可以直接读batch,没有额外的batch代价。列存是OLAP查询更好的选择。我们是否可以得出结论,对于OLAP查询使用向量化引擎,对于OLTP查询使用行引擎会更好。
5、对于不能向量化的查询捕获并抛出异常不是处理此类情况最安全和最有效的方法。在plan_tree_mutator中返回错误代码,并将此错误传播到上层可能会更好吗?
是的,至于效率,另一种方法是仅对某些plan节点进行向量化,而其他节点不向量化,通过在他们之间添加batch/unbatch节点来实现(这是你说的“在上层传播此错误”?)。正如您所提到的,这可能会带来额外的开销,还有其他好的方法吗?您说的最不安全是什么意思?PG catch接收ERROR,反馈给原始非向量化plan。
--hackers中对catching和忽略exception进行了多次讨论,不幸的是PG的PG_TRY/PG_CATCH机制不是高级语言C 、java等机制的变种。它不会执行堆栈unwind。如果在抛出错误之前获取了一些资源(files、locks、memory等),那么这些资源不会回收。仅回滚事务才能释放所有资源。实际上它发生在正常错误处理情况下。但如果捕获并忽略异常,视图继续执行,那么可能会导致更多问题。
可能在您情况下,这个不是问题,因为您确定错误发生在哪里,他是由plan_tree_mutator抛出的,并且看起来这个函数没有获得任何资源。但是在任何情况下setjmp开销都远高于对返回码的显式检查。因此,检查返回码实际上不会增加一些明显的开销,除了通过添加额外的检查使得代码复杂化。但是可以通过宏例如MUTATE来隐藏这些复杂度。
6、你测试过不同batch大小吗?我在VOPS中做了类似测试,发现大于128的大小并没有带来显著的性能提升。你当前使用batch大小是1024,它明显大于一页上元组数量。
好的,将对此进行一些实验
7、如何将向量化扫描和并行结合起来(9.6已支持)
目前还没实现。但这个想法与非并行的想法相同。复制当前并行扫描并实现向量化Gather,保持接口都是VectorTupleTableSlot。我们基本思路是复用当前PG执行逻辑大部分代码,然后进行向量化,并逐步进行性能调优。
--并行扫描时在并行worker之间分散页。为填充VectorTupleSlot,可能需要不止一页(除非你决定仅在单页中获取元组)。因此应该以某种方式考虑并行查询的具体请。还有用于并行查询的特殊节点,所以如果我们想为向量化操作提供并行执行,我们还需要用自定义节点替换这个节点。
做的一些性能测试
Q1的结果:
max_parallel_workers_per_gather | PG9_6, enable_vectorize_engine=off | PG9_6, enable_vectorize_engine=on | master (jit=on) |
---|---|---|---|
0 | 36 | 20 | 10 |
4 | 10 | -- | 5 |
与9.6相比,PG13在OLAP查询中提供了显著优势。当然并不意味着新版本的PG不需要向量化执行器。无论如何,我认为向量化执行器至于与列存结合才有意义。
Konstantin Knizhnik的测试
将vectorize_engine移植到master。花费的时间比预期要长:executor代码中很多东西都发生了改变:
par.warkers | PG9_6矢量化=关闭 | PG9_6矢量化=开启 | 主矢量化=关闭jit=打开 | 主矢量化=关闭jit=关闭 | 主矢量化=onjit=ofn | 主矢量化=onjit=off |
---|---|---|---|---|---|---|
0 | 36 | 20 | 16 | 25.5 | 15 | 17.5 |
4 | 10 | - | 5 | 7 | - | - |
因此,它证明了JIT提供与向量化执行器计划相同的加速理论(这这都消除了解释开销,但方式不同)。我仍然不确定我们是否需要向量化执行器:因为与当前的JIT版本相比,标准heap几乎没有任何改进。但无论如何,我们将使用列存zedstore或cstore对其进行测试。
列存的比较
par.workers | PG9_6vectorize=off | PG9_6vectorize=on | mastervectorize=offjit=on | mastervectorize=offjit=off | mastervectorize=onjit=on | mastervectorize=onjit=off | zedstore vectorize=offjit=on | zedstore vectorize=offjit=off | zedstore vectorize=onjit=on | zedstore vectorize=onjit=off |
---|---|---|---|---|---|---|---|---|---|---|
0 | 36 | 20 | 16 | 25.5 | 15 | 17.5 | 18 | 26 | 14 | 16 |
4 | 10 | - | 5 | 7 | - | - | 5 | 7 | - | - |
差距很小。
原文
https://postgrespro.com/list/id/CAB0yrenYmbYsioz167OrcO_8wVsvb=MA381-McLNcjEb1EJQYg@mail.gmail.com