pg_clickhouse 0.10增强查询下推,TPC-H Q17降至37毫秒
DataHot 速览
pg_clickhouse 0.10加入相关子查询下推、重构的纯C驱动和更多聚合函数。在22个TPC-H查询中,已有16个可完整下推;官方给出的Q17结果从32.7秒缩短到37毫秒。版本继续强化PostgreSQL查询ClickHouse时的透明加速能力。
为什么值得关注:查询下推覆盖率直接决定PostgreSQL与ClickHouse组合方案的实用性,这次升级包含可量化的性能进展。
译文
AI 逐段翻译在我们持续对pg_clickhouse进行投资的过程中,提高分析工作负载的下推覆盖率一直是我们的首要关注点,将TPC-H基准套件的全面下推作为我们眼前的衡量标准。自上次更新以来,我们取得了很大进展,六月包括TPC-H记分牌上的进展,自我们十二月的介绍性文章以来,我们一直没有真正提及这一点,所以我们将从这里开始。随着v0.10.0的发布,我们的记分牌从22个TPC-H查询中的12个完全下推提升到16个,仅剩6个查询即可完成整个集合。
在此过程中,我们还
- 在一个新的纯C客户端库上重建了二进制驱动程序,
- 使得可以下推的函数和聚合的数量翻了一倍以上,
- 并针对几个并发错误加固了二进制驱动程序,详情如下。
记分牌
现在有三个更多的TPC-H查询完全下推。这三个之前效率极低,因为由于查询的形状,pg_clickhouse不得不从ClickHouse单独获取每一行,然后在本地评估子查询(完整图表):
| 查询 | PostgreSQL | pg_clickhouse 0.3 | pg_clickhouse 0.10 | 下推 |
|---|---|---|---|---|
| Q2 | 588毫秒 | 3,446毫秒 | 24毫秒 | ✔ |
| Q17 | 2107毫秒 | 32,709毫秒 | 37毫秒 | ✔ |
| Q22 | 270毫秒 | 1,415毫秒 | 45毫秒 | ✼ |
(✔ = 整个查询是单个外部扫描) (✼ = 已下推,但要作为多个远程查询;通常是外部扫描加上一个InitPlan扫描。)
Q17是最大亮点:一个关联子查询,平均值l_quantity 每部分的数量,之前每个外部行都要针对600万行订单项(比例因子为1)评估一次,耗时32.7秒。完全下推后,仅需37毫秒。 这是三个数量级的差异,并且展示了pg_clickhouse在相同查询上胜过原生PostgreSQL自身计划(2.1秒)的明确案例。
还有六个查询未下推:Q13、Q15、Q16、Q18、Q20、Q21。Q16和Q18为我们指明了前进方向;pg_clickhouse 已经下推它们所需的SQL形状(IN和NOT IN作为反连接/半连接反解析,如Q2和Q17);阻碍它们的是PostgreSQL将它们子查询扁平化为反/半连接,其输入本身是连接,而反解析器尚不能在连接的两侧都遍历连接树。Q15和Q20遇到同样问题的变体。这就是子查询下推的下一个连贯部分。
完成子查询工作
十二月的头条功能是教会规划器将整个关联的EXISTS子查询作为单个LEFT SEMI JOIN下推,而不是每个外部行都进行一次ClickHouse往返的嵌套循环。这将22个TPC-H查询中的3个提升到12个。剩下的十个查询有一个共同点:规划器根本不能将子查询折叠成连接,因此留下了一个SubPlan。这是查询计划的一部分,它描述了作为完整查询执行部分运行的独立查询的完整计划,通常每行执行一次。将其下推是我们路线图上的第五项,我们已经将其完成(#289),在最新版本(0.10.0)中。现在,Postgres中的子查询变成了ClickHouse中的子查询:
1EXPLAIN (VERBOSE, COSTS OFF)2SELECT s.sale_id, s.amount FROM sales s3WHERE s.amount > (SELECT1.5*avg(s2.amount) FROM sales s24WHERE s2.item_id = s.item_id)5ORDERBY s.sale_id;1Foreign Scan on subplan_test.sales s2 Output: s.sale_id, s.amount3 Remote SQL: SELECT sale_id, amount FROM subplan_test.sales r1 WHERE ((r1.amount > (SELECT (1.5 * avg(q1_1.amount)) FROM subplan_test.sales q1_1 WHERE ((q1_1.item_id = (r1.item_id)))))) ORDER BY r1.sale_id ASC NULLS LAST4 SubPlan expr_15 -> Foreign Scan6 Output: ((1.5 * avg(s2.amount)))7 Relations: Aggregate on (sales s2)8 Remote SQL: SELECT (1.5 * avg(amount)) FROM subplan_test.sales WHERE ((item_id = {p1:Int32}))9(8 rows)该EXPLAIN仍然显示SubPlan节点(这只是PostgreSQL对相关性的记账),但你可以看到顶部的Remote SQL包含整个比较,包括子查询,全部在一个语句中发往ClickHouse。同样的机制使得pg_clickhouse能够下推整个TPC-H Q2:一个Foreign Scan和一次远程查询。NOT IN通过LEFT ANTI JOIN(v0.1.0的半连接的否定变体)获得相同处理,只要规划器能证明转换是安全的。
请注意,这些功能在ClickHouse 25.8以下版本中都不起作用,因为不支持关联子查询SQL形状;pg_clickhouse在计划时检查服务器版本,并在旧服务器上回退到本地评估,就像它始终对不支持的形状所做的那样。NOT IN的正确实现
下推SQL是简单部分。更困难的是确保它计算出与PostgreSQL相同的结果(#315,#317),这本身就是个深坑。ClickHouse的IN基于两值逻辑,PostgreSQL基于三值逻辑。这意味着x NOT IN (1, NULL)在PostgreSQL中可以是FALSE(x=1)或NULL,但绝不会是TRUE。天真地下推时,这些表达式在涉及NULL的比较中可能悄然反转结果,WHERE NOT IN返回PostgreSQL会过滤掉的行,GROUP BY将NULL组合并到FALSE,等等。这些都不会在普通测试中出现,这正是它在下推扩展中危险的原因;计划看起来正确,但输出微妙地错误。
V0.10的修复跟踪了每个表达式结果如何被消费,因此我们知道查询需要做多少防护以保持结果一致。这就像进入了门格海绵般的深坑:
- 过滤条件可以免费将
NULL视为FALSE,所以在NOT之外的条件中ClickHouse行为是好的。 - 值位置或否定需要额外的null检查,以将正确的Postgres值注入结果。
- 如果pg_clickhouse能证明操作数不可能为
NULL(通过追溯到非NULL常量,或具有NOT NULL约束且未被外层连接重新置空的列,或基于这些的非可空操作...),则可以跳过注入Postgres行为的防护。
因此,包含我们的防护和Postgres行为的查询看起来像这样:
1EXPLAIN (VERBOSE, COSTS OFF)2SELECT id FROM tnull WHERE xn NOTIN (1, NULL) ORDERBY id;1Foreign Scan on in_null_test.tnull2 Output: id3 Remote SQL: SELECT id FROM in_null_test.tnull WHERE ((CASEWHEN xn ISNULLAND notEmpty([1,NULL]) THENNULLWHEN countEqual([1,NULL], xn) >0THENfalseWHEN countEqual([1,NULL], NULL) >0THENNULLELSEtrueEND)) ORDERBY id ASCNULLS LAST4(3rows)而常见的无NULL情况可以以纯原生IN形式发送:
1EXPLAIN (VERBOSE, COSTS OFF)2SELECT id FROM tnull WHERE xn NOTIN (1, 500) ORDERBY id;1Foreign Scan on in_null_test.tnull2 Output: id3 Remote SQL: SELECT id FROM in_null_test.tnull WHERE ((xn NOTIN (1,500))) ORDERBY id ASCNULLS LAST4(3rows)后续工作(#317)将此行为推广到整个IN运算符家族(IN,NOT IN,= ANY,= ALL,<> ANY,<> ALL),包括标量形式和数组形式,允许它们无条件下推,而不是在无法证明非空性时回退到本地评估。
此外,我们有一个小错误:<> ANY(array)实际计算的是<> ALL,我们在处理该区域时将其修复。
所有这一切都基于ClickHouse的IN实际上按照我们描述的两值方式工作。一个服务器级设置(transform_null_in)可以改变这一点。因此,我们在默认的transform_null_in 0添加到pg_clickhouse.session_settingspg_clickhouse.session_settings,以确保 ClickHouse 服务器配置文件不会在我们刚才描述的防护措施下悄然破坏它们。
补充来源
1 个信源 · 1 篇报道这篇内容对你有用吗?
反馈只用于改善内容筛选,不等同于收藏