pg_clickhouse v0.10: 子查询下推和1000倍更快的TPC-H查询
继续我们对pg_clickhouse的投资,提高分析工作负载的下推覆盖范围仍然是我们的首要关注点,以TPC-H基准套件的全面下推作为我们的即时指标。自我们6月的上一次更新以来,我们取得了很大的进展,包括在TPC-H成绩榜上,我们自去年12月的介绍帖子以来其实没有详细讨论过,所以我们从这里开始。随着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毫秒 3446毫秒 24毫秒 ✔ Q17 2107毫秒 32709毫秒 37毫秒 ✔ Q22 270毫秒 1415毫秒 45毫秒 ✼(✔ = 整个查询是一个单一的外部扫描)(✼ = 下推,但作为多个远程查询;通常是外部扫描加上一个InitPlan扫描。)Q17是奖杯:一个关联子查询,计算每个部件的平均l_quantity,在规模因子为1时,每次对于600万个行项目评估,耗时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遇到同样问题。这是下推子查询的下一个连贯部分。12月的主要功能是教规划器将整个关联EXISTS子查询下推为一个单一的LEFT SEMI JOIN,而不是每个外部行进行一次ClickHouse的嵌套循环。这个变化将TPC-H查询的数量从22个中推进到12个。剩余的十个查询共享一个问题:规划器根本无法将子查询折叠成连接,因此留下了一个SubPlan。这个查询计划的部分描述了独立查询的完整计划,这个查询通常每行执行一次。将其下推是我们路线图上的第五项工作,我们在最新发布(0.10.0)中完成了它(#289)。现在,Postgres中的子查询成为了ClickHouse中的子查询:EXPLAIN (VERBOSE, COSTS OFF) SELECT s.sale_id, s.amount FROM sales s WHERE s.amount > ( SELECT 1.5 * avg (s2.amount) FROM sales s2 WHERE s2.item_id = s.item_id) ORDER BY s.sale_id; Foreign Scan on subplan_test.sales s Output: s.sale_id, s.amount 远程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 LAST SubPlan expr_1 -> Foreign Scan Output: (( 1.5 * avg(s2.amount))) 关系: Aggregate on (sales s2) 远程SQL: SELECT ( 1.5 * avg(amount)) FROM subplan_test.sales WHERE ((item_id = {p1:Int32})) ( 8 rows) EXPLAIN仍然显示SubPlan节点(这仅仅是PostgreSQL对关联的记账),但你可以看到顶部的远程SQL包含整个比较,包括子查询,在我们发给ClickHouse的一个语句中。相同的机制使pg_clickhouse能够完整下推TPC-H Q2:一个Foreign Scan和一个远程查询。NOT IN通过LEFT ANTI JOIN(v0.1.0的半连接的否定表亲)获得相同的处理,只要规划器能够证明这种转换是安全的。请注意,这一切都不适用于ClickHouse 25.8以下的版本,因为那不支持关联子查询的SQL形状;pg_clickhouse在计划时检查服务器版本,并在较旧的服务器上回退到本地评估,和它总是对不支持的形状所做的一样。下推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等等。这些在普通测验中都看不出来。
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡