十分钟成为 Contributor 系列 | 为 Cascades Planner 添加优化规则「建议收藏」

十分钟成为 Contributor 系列 | 为 Cascades Planner 添加优化规则「建议收藏」作者:崔一丁 到今天为止,“成为 Contributor 系列”已经推出了 “支持 AST 还原为 SQL”,“为 TiKV 添加 built-in 函数”,“向量化表达式”等一列活动。这一次借着 …

作者:崔一丁

到今天为止,“成为 Contributor 系列”已经推出了 “支持 AST 还原为 SQL”,“为 TiKV 添加 built-in 函数”,“向量化表达式”等一列活动。这一次借着 TiDB 优化器重构的契机,我们将这个系列再向着数据库的核心前进一步,挑战一下「为 TiDB 的优化器增加优化规则」,带大家初步体验一下可以对查询的执行时间产生数量级影响的优化器的魅力。

众所周知优化器是数据库的核心组件,需要在合理的时间内寻找到一个合理的执行计划,确保查询可以稳定快速地返回正确的结果。最初的优化器只有一些启发式的优化规则,随着数据量和业务的变化,业界设计出了 System R 优化器框架来处理越来越多的复杂 SQL 查询。它将查询优化分为逻辑优化和物理优化两个阶段,逻辑优化根据规则对执行计划做等价变形,物理优化则根据统计信息和代价计算将逻辑执行计划转化为能更快执行的物理计划。目前 TiDB 优化器采用的也是该优化器模型。

虽然 System R 优化器框架大大提升了数据库处理复杂 SQL 的能力,但也存在一定缺陷,比如:

  1. 扩展性不好。每次添加优化规则都需要考虑新的规则和老的规则之间的关系,需要对优化器非常了解的同学才能准确判断出新的优化规则应该处在什么位置比较好。另外每个优化规则都需要完整的遍历整个逻辑执行计划,添加优化规则的心智负担和知识门槛非常高。

  2. 搜索空间有限。搜索空间一方面因为优化规则难以添加导致比较狭小,另一方面,逻辑优化要求该优化规则一定在各个场景下都有收益才行,但在数据库面临的各种场景中,总有一些优化规则在某种数据分布下有收益,在另一种数据分布下没有收益,需要根据数据的分布估算代价来判断是否启用这些优化规则,因为这个原因,进一步导致一些优化规则不能添加到这个搜索框架中,或者添加后需要人工的通过开关来开启或关闭该优化规则。

为了解决上面的问题,更方便地添加优化规则,扩展优化器搜索空间,寻找更优的执行计划,我们基于 Cascades 优化器模型重新写了一个新的优化器,名字就叫 Cascades Planner。在这个优化器框架下,添加优化规则变得异常简单:

  1. 不用考虑优化规则之间的顺序关系,规则和规则之间完全解耦。

  2. 只针对特定的模式添加优化规则,不再需要遍历整个逻辑执行计划,不用熟知所有逻辑算子的功能,极大的降低了优化器的开发门槛。

在这篇文章中,我们主要聚焦在一条优化规则是如何工作以及如何给新优化器添加规则上,先让大家对这个优化器有一个直观的感受——“优化器没什么难的,不过如此”。下一篇文章,我们将更加详细的介绍 TiDB Cascades Planner 的原理和框架,供感兴趣的同学深入研究,如果大家等不及的话,可以先阅读下面的参考文献,提前了解一下 Cascades 优化器模型:

  1. The Cascades Framework for Query Optimization

  2. Orca: A Modular Query Optimizer Architecture for Big Data

  3. CMU SCS 15-721 (Spring 2019) : Optimizer Implementation (Part II)

Cascades 优化器简介

Cascades 优化器是 Goetz Graefe 在 volcano optimizer generator 的基础上优化调整之后诞生的一个搜索框架。这个框架有如下一些概念:

  • Expression:原论文中,Expression 用来指代包括 Plan 节点(也就是大家常说的的 SQL 算子)以及各种函数表达式(例如 MySQL 支持的 200 多个内置函数)在内的所有表达式。在 TiDB 现有框架实现中,只将 Plan 节点视作 Expression。Expression 大多会包含子节点,但每个子节点并不是一个 Expression ,而是一组等价的 Expression 集合,也就是接下来要介绍的 Group。

  • Group:表示等价 Expression 的集合,即同一个 Group 中的 Expression 在逻辑上等价。Expression 的每个子节点都是以一个 Group 表示的。在下文图例中,Group G0 中包含谓词下推前后的两个等价 Expression。

  • Transformation Rule:是作用于 Expression 和 Group 上的等价变化规则,用来扩大优化器搜索空间,也是本次要重点介绍的模块。下图 Group G0 中的第二组 Expression 便是第一组 Expression 经过了一个谓词条件(Filter)下推过连接(Join)的 Transformation Rule 之后新产生的 Expression

  • Pattern:描述一个执行计划的片段,注意这个 “片段” 不是 “子树”。这个片段可以是执行计划算子树中任意一段。每一个 Transformation Rule 都拥有自己的 Pattern,表示该 Rule 只作用于满足这个 Pattern 的 Expression。

  • Implementation Rule:将一个逻辑算子转换成物理算子的规则。如一个 Join 可以被转换成 HashJoin、MergeJoin、IndexNestedLoopJoin 等。每一个转换都由一个对应的 Implementation Rule 完成。

以查询 select * from t1 join t2 on t1.a = t2.a where t1.b > 1 为例,在经过 Cascades 优化器的谓词下推这一 Transformation Rule 后,搜索空间中的 Group 和 Expression 会是如下:

十分钟成为 Contributor 系列 | 为 Cascades Planner 添加优化规则「建议收藏」

目前 TiDB 中 Cascades 优化器的搜索过程大致如下:

  1. 首先将抽象语法树(AST)转换为初始的逻辑执行计划,也就是由 LogicalPlan 所表示的算子树。

  2. Cascades Planner 将这棵初始的 LogicalPlan 树等价地拆分到 GroupGroupExpr (Expression 在代码中对应的具体数据结构) 中,这样我们便得到了 Cascades Planner 优化器的初始输入。

  3. Cascades Planner 将搜索的过程分为了两个阶段,第一阶段是 Exploration ,该阶段不停地遍历整个 Group ,应用所有可行的 Transformation Rule,产生新的 Group 和 GroupExpr ,不停迭代直到没有新的 GroupExpr 诞生为止。

  4. 在第二个阶段 Implementation 中,Cascades Planner 通过对 GroupExpr 应用对应的 Implementation Rule,为每一个 Group 搜索满足要求的最佳(Cost 最低)物理执行计划。

  5. 第二阶段结束后,Cascades Planner 将生成一个最终的物理执行计划,优化过程到此结束,物理执行计划交给 TiDB 执行引擎模块继续处理。

一条优化规则如何工作

目前所有的 Transformation Rule 的实现代码都放在 planner/cascades/transformation_rules.go 文件中。我们以 PushSelDownProjection 为例,来简单介绍一条 Transformation Rule 的工作流程。

Transformation Rule 是一个 interface,该接口的定义如下(省去注释部分):

type Transformation interface {
	GetPattern() *memo.Pattern
	Match(expr *memo.ExprIter) bool
	OnTransform(old *memo.ExprIter) (newExprs []*memo.GroupExpr, eraseOld bool, eraseAll bool, err error)
}

代码100分

在 Cascades 中,每个 rule 都会匹配一个局部的 Expression 子树,这里的 GetPattern() 就是返回这个 rule 所要匹配的 Pattern。Pattern 的具体结构如下(省去注释部分):

代码100分type Pattern struct {
	Operand
	EngineTypeSet
	Children []*Pattern
}

这里需要提一下的是 EngineTypeSet 这个参数,因为有的算子比如 Selection ,既可以在 TiDB 执行,也可以在 TiKV 或者 TiFlash(一个列存引擎,目前尚未开源)Coprocessor 上执行。为了处理只在特定执行引擎上生效的规则,我们引入了这个参数。

Pattern 的构造可以借助 BuildPattern() 以及 NewPattern() 来完成。对于 PushSelDownProjection 这个规则来说,它起作用的执行计划 Pattern 是 Projection -> Selection

func (r *PushSelDownProjection) GetPattern() *memo.Pattern {
	return memo.BuildPattern(
		memo.OperandSelection,
		memo.EngineTiDBOnly,
		memo.NewPattern(memo.OperandProjection, memo.EngineTiDBOnly),
	)
}

Match() 函数是在命中 Pattern 后再做的一些更具体的判断,因为 Pattern 只包含了算子类型和算子树的结构信息。这时比如有一个 Rule 只对 inner join 生效,Pattern 只能判断到算子是 Join,要进一步判断它是否是 inner join 就需要依靠 Match() 函数了。对于大部分简单的 Transformation Rule,所以 Match() 函数只需要简单地返回 true 就行了。

OnTransform() 函数是规则的主要逻辑所在,在函数内部我们会创造新的 Expression 并返回合适的 eraseOld 以及 eraseAll 的值。举例来说,谓词下推会让计算尽可能提前,减少后续计算量,因此新生成地 Expression 一定是更好的选择,这时 eraseOld 就可以返回 true 。类似地,当一个 rule 返回的 Expression 一定比其他所有选择都更好时,比如某一个优化规则发现 a > 1 and a < 1 恒为假后,判断查询一定不会有结果产生所以生成了 TableDual 的新 Expression ,这时就可以让 eraseAll 返回 true 让优化器将同 Group 内的其他 Expression 全部清空。

PushSelDownProjectionOnTransform() 的行为如下图所示,简单来说:

十分钟成为 Contributor 系列 | 为 Cascades Planner 添加优化规则「建议收藏」

  1. 在初始时只有 G0G1 两个相关的 Group。

  2. 这个优化规则会将 Selection 推到 Projection 下面去,产生新的 Group G2,同时在 G0 中新增了 Projection->G2GroupExpr

如何添加一个 Transformation Rule

添加一个 Transformation Rule 简单来说就是编写一个新的结构体,实现 Transformation 这个 interface 的三个接口。Cascades 架构的优势就是它做了足够的抽象,让添加 Rule 的工作不需要考虑太多繁杂的事情。如果你在添加 Rule 时觉得有些地方写起来不是那么顺手,可以立刻停下手中的键盘来 #sig-planner 中和我们做一些讨论。

当然这里还是要列出一些注意事项方便大家在添加 Transformation Rule 时不走歪路:

  • OnTransform() 的函数头以及函数过程中添加充足的注释,说明自己的 Rule 做了一个什么样的变换,方便他人在读到这个 Rule 的代码时,能快速明白这个 Rule 做了哪些工作。

  • OnTransform() 函数中不要对原有的 Expression 做任何修改,因为原有 Expression 可能之后会继续触发其他的行为,如果做了修改,那么在下一次触发规则时,就可能产生一些意想不到的化学反应。

  • 要在 defaultTransformationRuleMap 中注册这个 Rule,这个 Map 是 TiDB 目前默认的 RuleSet。使用 RuleSet 的好处很多,比如针对 TP 和 AP 查询使用不同的 Rule 集合,使得优化器在处理 TP 查询时能快速做出不差的执行计划,在处理 AP 查询时多应用一些优化规则做出执行时间更短的执行计划等。

  • 单元测试必不可少。目前,Transformation Rule 的测试在 transformation_rules_test.go 中。测试函数的编写可以参考文件下的其他函数,主要是以跑一个完整的 SQL 进行测试。为了减轻修改测试输出的工作量,我们将测试输入输出单独记录在文件中,并可以通过命令行快捷更新输出。在添加了测试函数后,需要修改 testdata 目录下的 transformation_rules_suite_in.json 文件添加测试的输入,然后用 go test github.com/pingcap/tidb/planner/cascades --record 即可生成对应的 xxx_out.json 文件。记得要检查测试的输出是否符合预期,确保测试结果是自己想要的等价变换。

  • 目前 Cascades 优化器仍在早期阶段,偶尔会有一些框架性的改动可能会制造一些需要解决的代码冲突,还请大家理解。

如何成为 Contributor

为了方便和社区讨论 Planner 相关事情,我们在 TiDB Community Slack 中创建了#sig-planner 供大家交流讨论,之后还将成立优化器的专项兴趣小组,不设门槛,欢迎感兴趣的同学加入。大家在添加规则时遇到一些问题时,可以毫不犹豫的来 channel 里和我们吐槽~

参与流程:

  1. Cascades Tracking Issueporting the existing rules in the old planner 选择感兴趣的函数并告诉大家你会完成它。

  2. 添加一个 rule 并为其增加单元测试。

  3. 运行 make dev,保证所有 test 都能通过。

  4. 发起 Pull Request 并完成 merge 到主分支。

如果你有任何疑问,也欢迎到 #sig-planner 中提问和讨论。

原文阅读https://pingcap.com/blog-cn/10mins-become-contributor-20191126/

十分钟成为 Contributor 系列 | 为 Cascades Planner 添加优化规则「建议收藏」

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/10739.html

(0)
上一篇 2022-12-22
下一篇 2022-12-22

相关推荐

  • MongoDB 更新/删除/索引「建议收藏」

    MongoDB 更新/删除/索引「建议收藏」一、更新 语法 db.collection.update( , , { upsert: , multi: , writeConcer…

    2023-02-14
    159
  • Python TTK Represents:构建高效GUI界面的利器

    Python TTK Represents:构建高效GUI界面的利器Python 作为一种强大的编程语言,拥有广泛的程序库和工具,使其成为日益流行的编程语言之一。它有非常丰富的 GUI 库,使用这些库可以快速地构建出美观、高效的图形用户界面(GUI)应用程序。而 Tkinter 是 Python 的标准 GUI 库,很多 Python 开发者都使用它来创建 GUI 应用程序。但是,一些 Python 开发者并不喜欢 Tkinter 的样式,因此 Tkinter 的一种改进模块—ttk 库应运而生。

    2024-01-14
    93
  • Kylin on Parquet 介绍和快速上手

    Kylin on Parquet 介绍和快速上手Apache Kylin on Apache HBase 方案经过长时间的发展已经比较成熟,但是存在着一定的局限性。因此,Kyligence 推出了 Kylin on Parquet 方案。本文中,K

    2023-02-22
    146
  • Python List计数:快速统计数据出现次数

    Python List计数:快速统计数据出现次数Python是一种广泛使用的高级编程语言,用于广泛的应用程序开发,其语言特性之一是灵活而强大的数据结构支持。Python List是一个动态的数组,可以容纳不同类型的元素。Python List计数是指通过Python编写程序来找出列表中每个元素出现的次数,这种方法在数据科学,迭代和数据分析中非常常见。

    2024-03-12
    84
  • 【SQL】用SSMS连接Oracle手记

    【SQL】用SSMS连接Oracle手记情况: A机上有SSMS 18.x, B机上有SQL Server 2008 R2数据库, C机上有Oracle Database 11.2.0.4.0数据库 我想在A机用ssms连C机的oracle

    2023-02-15
    159
  • 【赵强老师】Redis的消息发布与订阅[通俗易懂]

    【赵强老师】Redis的消息发布与订阅[通俗易懂]Redis 作为一个publish/subscribe server,起到了消息路由的功能。订阅者可以通过subscribe和psubscribe命令向Redis server订阅自己感兴趣的消息类型

    2023-02-26
    142
  • 如何应对PS容差问题

    如何应对PS容差问题Photoshop是一个强大的图像编辑软件,用于进行各种图像处理和设计任务。在处理图像时,容差问题是一个常见的问题。如果您不知道如何处理容差问题,可能会导致图像质量下降,可能会对您的设计和制作工作产生不利影响。

    2024-06-13
    46
  • 围绕Python config的工程实践

    围绕Python config的工程实践Python config是指在Python开发过程中对配置文件进行管理,以便程序的配置参数更加灵活和易于修改。在复杂的工程中,往往需要 大量的配置参数,而这些参数的值往往具有临时性、不确定性,或者是需要根据启动环境的不同而变化。如果将这些参数耦合在代码里,则 会让代码变得难以维护,修改也非常麻烦,所以提供一个通用的配置框架,可以更好地促进开发的进行。

    2024-04-28
    79

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注