Balsa:Learning a Query Optimizer Without Expert Demonstrations【论文内容翻译】

Balsa是一个学习型查询优化器,它通过模拟学习和实际执行中的安全学习,不依赖专家优化器就能生成高效的执行计划。在JoinOrderBenchmark测试中,Balsa能在几小时内超越专家优化器。Balsa通过试错学习,从简单的模拟器开始,然后在实际执行中进行微调,利用超时和安全探索策略避免灾难性计划。其性能和泛化能力均得到显著提升,展示了在没有专家优化器的情况下学习优化查询的潜力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

标题:Balsa: 在没有专家演示的情况下学习查询优化器

***摘要***:查询优化器是每个数据库系统中性能关键的组件。由于它们的复杂性,优化器需要专家花几个月的时间来编写,花几年的时间来完善。在这项工作中,我们首次证明了在不向专家优化器学习的情况下学习优化查询是可能的和有效的。我们提出了一个基于深度强化学习的查询优化器 BalsaBalsa首先从一个简单的、环境不可知的模拟器学习基本知识,然后在实际执行中进行安全学习。在Join Order Benchmark测试中,Balsa只需两个小时的学习,就可以达到两个专家查询优化器(开源和商业)的性能,并且在工作负载运行时间的性能上在几个小时后就可以超过它们2.8倍。因此,在专家设计的优化器不存在的未来计算环境中,Balsa开启了自动学习优化的可能性。

介绍

查询优化器是每个数据库和查询引擎中的性能关键组件,它将声明性查询转换为高效的执行计划。这些优化器必须为每个查询导向候选计划的巨大搜索空间,通过利用有关数据的统计信息以足够的准确性对每个计划进行评分。

由于这种复杂性,优化器的开发成本很高。人类专家可能会花几个月的时间来编写第一个版本,然后花几年的时间来完善它。例如,PostgreSQL是世界上使用最广泛的数据库之一,其优化器在发布20多年后不断发生变化[6]。由于开发成本高,一些关系系统满足于基于启发式的优化,并推迟构建成熟的基于成本的优化器。例如,Spark SQL于2014年推出,但在2017年才添加了基于成本的优化器(CBO),而CockroachDB在经过“9个月的紧张努力”后,在v2.1中发布了其CBO的第一个版本[11]。

在本文中,我们询问是否有可能使用机器学习学习优化查询而无需向现有的专家优化器学习,而不是让人类专家花费数年时间开发最先进的优化器。通过设计和实现Balsa,我们肯定地回答了这个问题,Balsa是一个学习的查询优化器,可以匹配甚至超过专家构建的查询优化器(开源和商业)的性能。

Balsa利用了深度强化学习(RL),该技术已被成功用于学习复杂技能[3],并在玩游戏方面超越人类专家[26,27,33]。强化学习由一个agent组成,它通过与environment的反复交互来学习解决任务。agent观察environment的state,并采取action使reward最大化。如果这些actions带来了更好的rewards,它们就会被reinforced,也就是说,agent会被更新,使这些actions在未来更有可能发生。对于一个学习的优化器agent,比如Balsa,environment就是数据库;state是查询的部分计划;一个action是将算子添加到部分计划中,而完整计划的reward是其执行延迟(负值)。使用这个反馈循环,Balsa通过试错学习,在生成查询执行计划方面变得越来越好。

事实上,RL在查询优化方面的前景已经在最近的几个项目中得到了体现[13,16,17]。但是,这些方法假定有成熟的查询优化器可供学习。相反,Balsa不会向这样的专家优化器学习。据我们所知,Balsa首次证明了在不向专家优化器学习的情况下学习优化查询是可能的和有效的。这可能会产生深远的影响,因为它为在不存在成熟优化器的新数据系统中自动学习优化铺平了道路[19,20]。

在没有专家优化器指导的情况下学习优化查询的一个唯一挑战是,大多数查询的执行计划都很慢,有时比最优计划要高几个数量级[14,15]。在学习过程的开始,agent没有先验知识,因此选择这种灾难性计划的概率很高,这可能会阻碍任何进展。这是查询优化的一个独特特征,其他成功的RL应用程序(如游戏)没有共享这个特征。事实上,在大多数游戏中(如AlphaGo [26], MuZero[23]),一个“糟糕”的action通常会导致游戏更快结束。因此,在这些环境中,差action不会阻碍学习。

为了避免灾难性的计划,Balsa采用了从模拟到现实的学习[30]。在“模拟”阶段,Balsa从模拟器中快速学习如何在不执行查询的情况下避免灾难性的计划,而在“现实”阶段,它从实际执行中学习以生成高性能的计划。模拟器通过使用带有基数估计器的基础的纯逻辑成本模型agent提供成本反馈。为了方便起见,我们使用了PostgreSQL的基数估计器,一种简单的基于直方图的方法[14]。我们选择一个现有的估计器,因为与优化器不同,基数估计器与执行环境无关,因此相同的估计器可以用于任何环境。(在我们的评估中,我们对另一个商业引擎使用了PostgreSQL的估计。)此外,估计器不需要高质量才能进行有效的仿真。事实上,PostgreSQL的估计可以显示出数量级的误差[14],我们发现即使在这些估计中注入噪声也不会影响Balsa的性能(§10)。这是因为Balsa只是使用模拟来学习如何避免灾难性的计划,而不是达到专家级别的性能。因此,基本的成本模型和估计就足够了。

接下来,为了更大改进从模拟器获得的不完善的知识,Balsa通过实际执行查询在真实环境中学习。虽然模拟知识使agent能够避免最差的计划,但它仍然可能偶然发现糟糕的计划,导致学习过程中不可预测的停顿。Balsa通过使用timeout解决了这个问题。查询的timeout被设置为迄今为止在学习期间的最佳延迟。如果一个计划超时了,我们给它分配一个预定义的低奖励(因为我们不知道它的真正奖励)。如果计划完成,我们将收紧未来迭代的timeout。因此,timeout限制了每个学习迭代的运行时间,确保安全执行,消除了不可预测的停顿。

最后,RL agent必须平衡利用过去的经验和探索新的经验,以避免局部最小值。经典的解决方案是随机探索,即偶尔选择一个随机计划。不幸的是,这种标准策略是无效的,因为搜索空间中的随机计划可能非常昂贵。相反,Balsa从一系列可能是好的计划中进行探索。在探索过程中,Balsa生成几个最佳预测计划(而不是最佳计划),然后从中选择最佳的未见过计划。这种安全的探索方法提高了Balsa的计划覆盖率和性能。

给定目标数据集,Balsa通过反复优化一组样本查询来进行训练。训练后,我们在同一数据集的一组新的未见过查询上测试其泛化性能。我们发现模拟学习、安全执行、安全探索这三个组成部分都促进了它的泛化。它们将Balsa暴露于数量更多、种类更多的计划中,从而使它能够更健壮地优化新的查询——我们认为这是实际部署学习优化器所必需的特性。我们进一步提出使用多样化的经验来增强泛化(§6)。我们在评估(§8.2,§8.5)中深入研究了Balsa的泛化,发现它在未见过的查询上取得了比两个专家优化器更好的性能。

我们称我们的方法为“Bootstrap, Safely Execute, Safely Explore”,因此简称为Balsa。据我们所知,Balsa是第一个不依赖于现有专家优化器生成的计划(演示)的学习型优化器。在Join Order Benchmark[14]这一复杂的工作负载上,Balsa经过两个小时的训练就能达到两个专家优化器的性能,再经过几个小时的训练,其性能就能达到专家优化器的2.1-2.8倍。

综上所述,我们做出了以下贡献:

  • 我们将引入Balsa,这是一个学习查询优化器,它不会向现有的专家优化器学习。

  • 我们设计了一种简单的方法来学习查询优化器,而不需要专家示范:bootstrapping from simulation (§3), safe execution (§4), safely exploring the plan space (§5)。

  • 我们提出了多样化的经验,这是一种新的方法来进一步提高训练和泛化性能(§6),包括泛化到具有高度不同连接模板的未见过查询。

  • 经过几个小时的训练,Balsa可以超越开源(PostgreSQL)和商业查询优化器(§8)。

  • 我们表明,尽管没有向专家优化器学习,但Balsa的性能优于现有的最先进技术。

Balsa开源在: GitHub - balsa-project/balsa: Balsa is a learned SQL query optimizer. It tailor optimizes your SQL queries to find the best execution plans for your hardware and engine.

1.1 Differences from Prior Work

为了突出Balsa的贡献,我们简要地比较了最相关的工作,并将完整的讨论推迟到§9。

DQ[13]从专家优化器的成本模型中学习。因此,它的性能受到成本模型质量的限制,这可能会不准确的。Neo[17]采取了相反的方法,从专家优化器的计划和实际执行中学习。虽然这比仅仅使用成本模型更准确,但它也更昂贵。重要的是,这些解决方案要么采用专家成本模型,要么采用专家优化器进行引导。

相比之下,Balsa既不需要专家成本模型(如DQ)也不需要专家优化器(如Neo)来学习。Balsa通过从一个最小的、只有逻辑的成本模型中进行引导,然后在实际执行中进行安全学习,从而消除了这些基本假设。对于成本模型,Balsa需要一个基本的基数估计器(§3.3)。我们发现不准确的估计仍然可以导致成功的模拟,并且大多数Balsa的知识都是在模拟之后学习的(§10)。

总之,本文解决了在没有专家优化器的情况下学习优化的新问题。(我们在§10中讨论了Balsa如何比以前的工作更好地利用专家,如果有的话。)为了解决这个问题,我们开发或应用新的技术到学习优化器领域。这些包括sim-to-real (§2), safe execution (§4.3), safe exploration (§5), on-policy learning (§4.1), enhancing generalization with diversified experiences (§6)。

BALSA 概述

Balsa的目标是学习为给定的数据集和执行引擎优化查询。我们假设有可用的训练工作负载。在测试时,Balsa被要求优化针对同一数据集定义的未见过的查询,这些查询可以包含新的过滤器和连接图,这些与训练查询中的查询不同。

Balsa通过试错来学习。它优化训练查询,生成不同的计划,然后在引擎上执行它们以观察它们的运行时间。基于运行时间反馈,Balsa自我更新以纠正错误并奖励正确的决策。随着反馈循环的重复,Balsa在制定好计划方面做得越来越好。

经过训练后,可以部署Balsa来优化一个未见过的查询测试集。通过生成的训练计划的性能、生成的测试计划的性能(即它的泛化能力)和它的学习效率来评估agent。

在整个学习过程中,Balsa访问底层执行引擎只是为了执行计划和观察它们的运行时间,而不是从现有的优化器中学习。许多数据系统在优化器出现之前很久就有了执行引擎,这一事实说明了这一需求(§1)。

假设:我们假设数据库内容保持静态。可以通过重新训练来处理对模式的追加或即时更新的更新。这个假设意味着agent不需要解决具有变换分布的学习问题。另一个假设是,Balsa目前优化了select-project-join (SPJ) 块。这与将查询分解为简单的SPJ块并逐块优化的经典处理方法[24]是一致的。

2.1 Approach

Balsa的体系结构如图1所示。它由三个基本组件组成:在最小成本模型中引导值网络在实际执行中微调值网络,以及使用树搜索算法构建查询计划

  • Classical design: cost models + enumeration

经典的优化器设计[24]使用专家实现的成本模型,该模型接受计划并输出成本估算:

C : plan → cost

成本的设计是为了反映真实的执行性能:较低的成本应该与更快的执行相关联。优化器通过枚举候选计划并使用成本模型对它们进行评分来生成计划。对于具有少量表的查询,通常使用动态规划(DP)作为枚举模块。

  • RL: value functions + planning

与估计计划的即时成本的成本模型不同,Balsa学习了一个值函数,当计划被用作部分步骤(子计划)时,它可以估计执行查询的总成本/延迟:

V : (query, plan) → overall cost or latency

给定一个值函数,我们可以使用它自下而上地构建一个计划来优化查询。考虑一个连接表{A, B, C, D}的查询Q。为了找出执行最佳的第一次连接,我们比较了所有有效的第一次连接的总成本/延迟,即值:{A, B, C, D} ⇒ [V(Q, A⋈B)); V(Q, A⋈C); . . .]

换句话说,我们使用V对2-表连接进行评分,这些连接都是完成查询Q的部分子计划。最佳的第一个连接是具有最低V值的连接。假设A⋈C是其中最好的,那么我们可以继续这个过程,给所有可能的二次连接打分:{A⋈C, B, D} ⇒ [V(Q, B⋈D); V (Q, B⋈(A⋈C)); . . . ]

继续这样的planning将生成一个完整的查询计划。

与经典的成本模型相比,值函数直接针对完成查询的最终总体成本/延迟进行优化——这是我们关心的真正目标。此外,学习的值函数可以利用数据来定制目标数据库和硬件环境,潜在地超越了启发式。如果最优值函数V*是已知的,那么planning将为查询生成最优计划。我们的目标是尽可能精确地近似V*。

  • Learned value networks

Balsa通过在agent-collected的数据上训练神经网络V_{\theta } (query, plan)(带参数θ)来逼近最优值函数。网络的两个输入分别被特征化为查询特征(编码连接表和过滤器)和计划特征(编码计划的树结构和每个节点的算子类型)。

我们分两个阶段学习值函数。首先,我们在最小成本模型支持的快速模拟环境中学习参数\theta _{sim}。接下来,我们初始化参数\theta _{real}\theta _{sim},并开始在实际执行中微调值函数。这两个阶段产生了值网络:

训练后,V_{real} 结合planning一起用于优化新查询。

  1. Step 1: bootstrapping from a minimal cost model (§3)
    Balsa开始在查询优化的“模拟器”中学习,也就是成本模型。使用模拟器的主要优点是,agent可以了解灾难性的计划,而无需在学习的初始阶段执行它们。agent根据不准确但快速查询的成本模型引导初始知识,该模型为agent提供快速反馈(成本估计)。成本模型是通用的,不针对目标引擎或硬件建模。
    为了训练simulation model V_{sim},我们使用数据收集过程(例如DP)来枚举训练查询集的计划,并向模拟器询问成本。每个查询可以产生数千个训练数据点,最终产生一个足够大的数据集,D_{sim} = {(query, plan, overall cost)}。然后以标准的监督学习方式在该数据集上训练V_{sim}

  2. Step 2: fine-tuning in real execution (§4)
    接下来,我们将值函数从在模拟器中表现出色转移到在实际执行环境中表现出色。第二阶段从经过训练的仿真模型初始化实际执行模型开始:V_{real} ← V_{sim}V_{real} 的微调是在查询执行和模型更新的迭代中执行的。在每次迭代中,Balsa使用当前的V_{real} 来优化训练查询;这些计划在执行时测量了它们的延迟。然后,Balsa根据这些收集到的数据更新其V_{real},使其延迟预测更加准确。
    在实际执行中学习的一个关键挑战是减轻缓慢的计划。我们的处理方式如下。通过从V_{sim}初始化,Balsa在iteration 0中的行为将比随机初始化(相当于随机选择计划)要好得多。在iteration 0之后,Balsa使用timeouts(由早期运行时间确定)来提前终止慢计划(§4.3),并且还使用安全探索(§5)。

  • Planning with tree search

Balsa在学习的值函数之上使用树搜索计划来优化查询。学习V_{real} 引导搜索到计划空间的有前景的区域。随着V_{real} 变得更加精确,可以找到更好的计划。

有许多具有不同复杂性-最优性权衡的树搜索算法:从greedy planning到高级planning algorithms,如蒙特卡洛树搜索(Monte Carlo tree search)。我们选择一个中间方式,通过使用简单的波束搜索(beam search)(§4.2)。

在接下来的章节中,我们将详细描述Balsa的组件。

从模拟过程中引导

训练的第一阶段旨在快速向agent传授基本知识,然后让它在长时间的实际执行中开始学习。我们通过在最小模拟器(即成本模型)中引导Balsa来实现这一点。它“模拟”没有实际执行的查询计划中的查询优化。相反,agent向模拟器发出大量的计划,模拟器可以快速返回成本估计(而不是测量它们的运行时间)作为反馈。

  • Why is a simulator necessary?

查询的搜索空间很大,灾难性的执行计划也很多[14]。不幸的是,灾难性的计划可能会阻碍学习进程:agent可能会等待很长时间才能完成执行缓慢的计划,然后才知道这是一个糟糕的action(如果它完成了)。这一属性与其他RL用例(如游戏)形成了直接对比。在游戏环境中(如围棋、国际象棋、雅达利),糟糕的移动通常会导致游戏提前结束,因为对手可以利用agent的错误。

没有经过模拟训练的随机初始化强化学习agent很容易遇到这种灾难性的计划,特别是在学习的早期阶段。我们用一个简单的实验来证明这一点:我们在没有模拟学习的情况下随机初始化6个agent,并让它们优化来自Join Order Benchmark的94个查询(详细设置见§8.1)。由中位数随机agent生成的计划在工作负载运行时的执行速度比由专家优化器PostgreSQL生成的计划慢45倍。最慢的agent比专家慢79倍(2.5小时vs. 2分钟)。

接下来,我们描述了所采用的成本模型的具体选择。

3.1 A Minimal Simulator

Balsa使用了一个最小的、逻辑的仅计划成本模型,它抓住了“更少的元组导致更好的计划”的一般原则。它是最小的,因为它不需要任何关于执行引擎和物理算子的先验知识(例如,合并与散列连接)。

形式上,我们使用C_{out} 成本模型[5]:C_{out}(T) = 公式(具体见原文)。

其中,|T| 表示从基数估计器(§3.3)获得的表(考虑了过滤器)或连接的估计基数。该成本模型通过将所有算子的估计结果大小相加来估算查询计划的成本。

  • Tradeoffs of a minimal simulator

我们选择了一个最小成本模型,以尽可能少地引入先验知识。模拟学习的目标是引导agent远离绝对灾难性的计划(当它开始真正的执行阶段时),而不是载入专家知识。它也是通用的:通过不建模物理细节,它可以用来引导任何引擎的Balsa优化。

由于其简单性,成本模型本质上是不准确的。Balsa将在实际执行阶段微调时学习来填补缺失的知识和纠正不准确(§4)。正如我们将在§8.3.1中所示,虽然Balsa可以利用预先设计的、更复杂的成本模型来加速训练,但它们不是Balsa达到专家级性能所必需的。

3.2 Simulation Data Collection

给定一个模拟器,我们通过应用批处理数据收集过程从中提取尽可能多的知识。输出为模拟数据集D_{sim}= {(query, plan, overall cost)},用于训练值网络V_{sim}。具体来说,我们使用动态规划(DQ[13]也使用)来收集数据。

  • Enumerating plans using dynamic programming

对于Balsa训练工作负载中的每个查询,我们使用密集的计划空间运行经典的Selinger[24]自下而上的DP。它首先枚举由基表扫描组成的所有有效的2-table 连接的最佳计划,然后枚举3-table连接等。每个枚举的计划T将从成本模型中获得成本估计C,生成一个数据点(query=T , plan=T , overall cost=C),其中query=T表示仅限于T的表/过滤器的原始查询。该数据点经历如下所述的数据扩充过程,以产生要添加到D_{sim} 中的训练数据点的列表。

数据收集是高吞吐量的:数据是从所有枚举的计划中生成的,而不仅仅是从最终DP结果中的最优计划集生成的。这意味着包含了一些次优计划(在成本模型下),这增加了数据的多样性并有助于学习。图2说明了数据收集过程。

但是,对于连接多个表的查询来说,DP的运行时间可能会变得太大。因此,我们跳过从 ≥n 个表(我们设置n = 12)的查询中收集数据。也可以采用其他策略。例如,DQ提出了一个局部DP方案,其中运行DP的前j个层级,其余层级进行贪婪规划。

  • Data augmentation

Balsa采用DQ提出的数据增强技术,其中从单个枚举计划生成多个数据点。具体来说,给定一个(query=T , plan=T , overall cost=C), T 的每个子计划T' 将产生一个不同的数据点,具有相同的“总查询”T 和相同的成本:{(query=T, plan=T',overall cost=C):∀T′ ⊆ T}。该技术极大地丰富了数据集D_{sim}的数量和种类。

  • Interpretation

在RL术语中,增强技术反映了轨迹(总体查询/最终计划)中的所有状态(子计划)共享相同的回馈,因为中间奖励被定义为0,而终端奖励是最终计划的负成本。

3.3 Discussion

我们发现模拟学习非常有效。在§3的开始,我们进行了一个简单的实验,说明随机初始化(即,无引导)agents和专家优化器之间的差距高达79倍。现在,通过模拟引导,agents在不执行任何实际执行的情况下,显著缩短了这一差距,最多只比专家慢5.8倍。

  • Cardinality estimator

模拟器需要一个基数估计器。如§1所述,我们选择PostgreSQL的估计器是因为它的简单性(每列直方图;启发式地假定连接是独立的;复杂过滤器的“魔法常数”)[14]。Balsa没有学习PostgreSQL的优化器(成本或计划)。

我们使用现有的、教科书式的估计器是为了方便,而不是依赖它来获得良好的性能。事实上,大多数Balsa的质量改进是在模拟阶段之后学习的(§8.2,§10)。

  • Alternative cost models

虽然Balsa提倡最小的模拟器,但如果需要,用户可以插入更多的先验知识。其他成本模型可能包括越来越多的物理算子知识(例如,内存设置的C_{mm}成本模型[14])。针对不同目标(例如,更低的内存占用)优化的新查询引擎可以引导带有C_{out} 的Balsa(通常适用其“元组越少越好”的原则),或者开发针对目标量身定制的另一个最小成本模型。

从实际执行中学习

模拟学习向agent传授基本知识。但是没有任何模拟器可以完美地反映真实执行环境的细微差别。因此,我们通过在真实环境中执行查询来微调agent

4.1 Reinforcement Learning of the Value Function

Balsa学习实时执行值网络,V_{real}(query, plan) → overall latency,使用强化学习。其基本思想是agent迭代地使用其当前值网络来优化查询并运行它们,然后使用延迟反馈来改进自身。当这个反馈循环运行时,会收集到更多的执行数据,并且agent的V_{real} 会更好地生成好计划。

具体地说,我们从V_{sim}初始化的V_{real} 和一个空的实际执行数据集 D_{real}=∅ 开始。学习的每次迭代由执行和更新阶段组成。

  • Execute

agent使用当前的V_{real} 来优化每个训练查询q,生成执行计划p。(计划将在§4.2中描述。)每个计划都在目标引擎上执行,并测量其延迟 𝑙。这将产生一个数据点(query=q, plan=p, overall latency=𝑙),然后进行与§3.2中讨论的相同的subplan数据增强以产生数据点列表:

  • Update

Balsa使用收集到的数据来改进它的V_{real} 。我们执行随机梯度下降(SGD),在预测延迟和真实延迟之间使用L2 loss。因此,错误的预测得到纠正,好的预测得到加强。数据点(q,p,𝑙)从D_{real} 中采样。然而,模型输出V_{real}(q, p)不是向 𝑙 更新,而是向查询q迄今为止获得的最佳延迟更新,其中涉及子计划p ——一种先前提出的技术[17]。延迟标签校正的动机如下。考虑查询q连接表A、B、C、D。子计划 p = Join(A, B)可能在两次执行中出现,一次是C接在后面,一次是D接在后面。它们可能有非常不同的延迟,比如1秒和100秒。由于我们希望最小化延迟,我们将较低的延迟 𝑙 = 1定义为子计划p的值,因为p可以使q运行得这么快。到目前为止,最佳延迟是从整个D_{real} 计算出来的。

因此,数据收集和价值函数改进是交替进行的。该算法可以被认为是值迭代[29]或专家迭代[4],它的变体最近已经应用于先前的工作中,如查询优化[17](与Balsa的更新、重置和重新训练跨迭代的价值网络不同)、定理证明[21]和计算调度优化[2]。

  • On-policy learning

Balsa通过使用on-policy learning,在上述算法的基础上采用了一种新的优化方法。对V_{real} 的更新只在当前V_{real} 生成的数据点上执行。换句话说,SGD是对数据点(q, p, _)执行的,这些数据点是从数据集D_{real} 的最近迭代中采样的,而不是从整个数据集中采样的。后者将产生许多迭代之前的数据,因此是off-policy。标签校正仍然使用整个数据集。

直观地说,最近的数据点通常对agent来说是最令人惊讶的,并且具有更快的延迟标签,因此关注它们应该是有益的。事实上,我们发现on-policy learning可以通过减少每次迭代的SGD步骤的数量来显著加速学习,并改善Balsa的计划多样性和性能(§8.3.4)。On-policy learning使Balsa的训练速度比Neo[17]快9.6倍以上,Neo是一种先进的方法,采用了完整的再训练方案(§8.4)。我们假设这项技术也可以改善预测运行时间的值函数的其他应用。

4.2 Plan Search

对于学习值网络,Balsa使用简单的(最佳优先)波束搜索为给定查询生成执行计划。

波束搜索对搜索状态进行操作,每个状态都有一组部分计划进行查询。搜索从包含查询中的所有表(扫描)的根状态开始。大小为b的波束存储了待扩展的搜索状态,并根据它们的预测延迟进行排序。在每一步中,从波束中弹出最佳搜索状态,并应用所有可用的actions来产生子状态。每个action使用分配的物理连接算子连接当前状态下的两个符合条件的计划,如果任何一方是表,则分配扫描算子。由于搜索状态是一组部分计划(连接关系和非连接表),因此对其应用actions将导致至少一个完整的计划。

然后,所有产生的子状态由价值网络V_{real} 评分并添加到波束中,只保留前b个状态。通过这种方式,学习价值网络引导搜索集中在计划空间中更有前景的区域。当找到k个完整计划时,波束搜索终止。Balsa使用b = 20和k = 10。

  • Top-k plans and exploration

波束搜索不能保证返回全局最优计划,在搜索过程中可能会发现更好的计划。因此,我们继续搜索,直到找到k个完整的计划。在测试时,将发出该列表中的最佳计划。

有趣的是,在训练时,获得计划列表可以使简单的探索技术放在首位。我们认为所有这些计划都具有合理的最优性,因此在它们之间进行探索应该是安全的,并优先选择未见过的计划作为波束搜索输出。这一技术将在§5中讨论。

4.3 Safe Execution via Timeouts

查询优化中的一个独特挑战是,在庞大的搜索空间中,即使存在快速计划,也会出现昂贵计划的激增。当Balsa从实际执行中通过试错来学习时,它可能会遇到具有不可接受的高延迟的长时间运行计划。

Balsa通过应用超时(分布式系统中的经典思想)解决了这一挑战。由于训练在迭代中进行,因此已知相同训练工作负载的早期执行运行时间,并且可以用于绑定未来的迭代。

该机制的关键是如何选择初始超时。幸运的是,模拟学习允许我们假设,当真正的执行开始时,为一组训练查询生成的第一个计划具有合理的(尽管不是最优的)延迟。

  • Timeout policy

在iteration 0’s 执行阶段(就在模拟学习之后),允许计划完整地完成执行——假设模拟学习产生一个非灾难性的起点。将每个查询记录的最大运行时间设为T。

对于迭代 i > 0,对所有agent生成的计划应用 S × T 的超时,其中S为“松弛因子”。根据T的定义,对于任何训练查询,都存在一个可以在T时间内完成执行的计划。松弛的目的是给一些额外的空间,并考虑运行时间的变化(Balsa使用S = 2)。

如果一个计划的执行时间超过了当前超时时间,那么它将提前终止,因为对于相同的查询,它将比之前发现的计划慢。它被分配了一个大的标签,而不是它真实的未知延迟。如此大的标签会阻碍并引导agent在未来的迭代中远离类似的计划。

超时会逐渐收紧。如果迭代以最大每个查询运行时间T' < T结束,则下一个迭代的超时收紧为 S ×T'。此改进确保超时既不会太小(这会阻碍改进),也不会太大(会浪费努力)。它为agent生成了一个隐式学习课程。

总之,我们发现超时机制可以显著加快学习。它限定了每个迭代执行阶段的运行时间,并消除了意外的停顿,从而实现了安全执行。

在实际执行中安全探索

当RL agent利用其过去的经验来获得良好的性能时,它还必须探索新的经验来避免局部最小值。为了实现这一目标,可以使用一种探索策略。

然而,大量的慢计划(查询优化的一个独特特征)还需要安全的探索,即避免灾难性的计划。从搜索空间中抽样的随机计划是缓慢的[14],选择探索它们会再次阻碍学习。在我们早期的实验中,一个基本的ϵ-greedy策略(对于每个训练查询,以一个小的ε概率对一个随机计划进行采样,就像QuickPick[34])经常选择导致超时的较差计划,从而减慢了更好计划和学习的发现。

为了实现安全探索,Balsa提出了一种简单的基于计数的探索技术。从本质上讲,这一系列方法鼓励agent探索较少访问的state或执行较少选择的action。我们用下面的方式来实例化这个原则。

  • Count-based exploration for beam search

我们的目标是为agent提供一个合理规划的“信任区域”供其探索。为了做到这一点,波束搜索被要求返回top-k个计划,按照预测延迟升序排序,而不是找到一个最佳计划。我们不是执行最佳计划(即,具有最低的预测延迟),而是执行此列表中最佳的未见计划。如果所有top-k计划之前都已经执行过——这表明有足够的探索——Balsa就会通过执行预测的最低价的计划来进行利用。计划的访问计数由hash表缓存,这增加了较低的开销,因为过去的执行已经存储在D_{real} 中。图3使用示例统计(k = 3)说明了这种技术。

直觉上,所有的top-k计划都可能是好的(因为它们是由价值网络引导的波束搜索产生的),所以不应该严格根据它们的预测延迟(这是不完美的估计)来选择它们。因此,在这个“信任区域”执行新颖的、未见过的计划既安全又具有探索性。

多元化的经验

对于学习查询优化器,稳健地优化未见过的查询是必不可少的。为了进一步提高Balsa的泛化性能,我们引入了一个简单的方法,多样化的经验

  • Problem: mode diversity

当使用价值网络来指导计划搜索时,agent倾向于只经历其价值网络所偏好的计划,并可能逐渐收敛到具有相似特征的计划,或称为“模式”[36]。例如,如果散列连接和循环连接对工作负载同样有效,则agent可能会学习大量使用散列连接,而另一个agent可能更喜欢循环连接。任何一个agent都可以输出好的计划,因为两个算子都是有效的,但它们可能缺乏关于计划的知识,而这些计划更喜欢替代算子或形状。(虽然探索增加了计划的多样性,但新的计划仍然相对局限于单一agent的模式。)低模式多样性可能会阻碍agent对高度不同的、未见过的查询的泛化,这些查询需要不熟悉的模式才能很好地优化。

  • Diversified experiences

为了增强泛化,我们建议简单地合并由几个独立训练的agents(具有不同的随机种子)收集的经验(D_{real}),并在没有任何实际执行的情况下重新训练一个新的agent。图4说明了这个过程。我们的看法是,这种多元化的经验涵盖了多种模式。因此,对其进行训练可以产生一个更健壮的价值网络,泛化效果更好。

表1证实了这一观点:独特计划的数量几乎与agents的数量呈线性增长,这表明不同agents所经历的计划确实高度多样化。我们发现这个简单的方法是有效的(§8.5),提供了一种交换更多计算的方法,当可用时,以获得更好的性能。

BALSA实现

在本节中,我们将详细描述Balsa的训练设置。在高层次上,要在新引擎上操作Balsa,它需要以下几点:

  1. 执行环境(执行计划;支持超时)。

  2. 搜索空间的定义(一组查询算子和组成它们的规则)。

  • Optimizations

我们通过并行数据收集、计划缓存和流水操作来优化训练。使用Ray[18],将查询执行分派到一个相同的虚拟机池,每个虚拟机都运行目标数据库的一个实例。每个虚拟机一次执行一个查询,以防止干扰。

使用计划缓存可以快速查找重新发布的计划之前的运行时间,并且可以跳过重新执行。每次迭代中的规划和远程查询执行都是流水线式的(图5):一旦树搜索(由主agent线程运行)完成了训练查询的规划,就会发送输出计划进行远程执行,然后开始为下一个查询进行规划。因此,这两个阶段是重叠的。在执行价值网络更新之前,agent将等待所有计划完成。

  • Value network details

价值网络V_{sim}V_{real} 被实现为简单的树卷积网络[17](0.7M个参数,即2.9MB)。我们还尝试在早期使用Transformer[32]实现它们;结果发现,这种方法同样有效,但计算成本更高。当训练或更新价值网络时,我们抽取10%的经验数据作为提前停止的验证集。价值网络的输入、query和plan编码如下。每个计划的编码与Neo[17]相同。查询的特征是一个向量[table→selectivity],其中每个slot对应一个表,并保存其估计的选择性(§3.3)。absent tables的slots用0填充。这种编码比Neo和DQ[13]都要简单。

实验

我们对Balsa进行了深入的评估。我们的主要发现是:

  • 通过试错的学习,Balsa生成了更好的执行计划,在工作负载上的运行时间比两个专家优化器PostgreSQL和“CommDB”快2.1 - 2.8倍(§8.2)。

  • Balsa需要几个小时来超越专家优化器,再花几个小时来达到测试工作负载的峰值性能(§8.2)。

  • Balsa优于从专家演示中学习的方法[17],这是一种先进的方法,尽管它没有向专家优化器学习(§8.4)。我们还发现,在这种先前的方法中,不良的泛化是潜在的失效模式。

  • 多样化的经验显著增强了泛化,包括对具有高度不同的连接模板的查询(§8.5)。

  • Balsa学习算子和计划形状的新偏好(§8.6)

此外,在§8.3中,我们进行了详细的消融研究,以了解Balsa设计选择的影响。

8.1 Experimental Setup

  • Workloads:

  1. Join Order Benchmark (JOB)

  2. TPC-H

  • Expert baselines and engines:

the optimizers of two mature expert systems: PostgreSQL (12.5; open-source) and CommDB (a leading commercial DBMS; anonymized [22])

由于搜索空间较小,Balsa在JOB工作负载上训练了500次迭代,在TPC-H上训练了100次迭代。Balsa使用前面章节中讨论的所有组件和默认值。

  • Expert performance:

我们遵循Leis等人[14]的指导,创建所有主键和外键索引,使我们的基线运行JOB的速度比以前的工作快得多[17,31]。这也使得搜索空间更加复杂和具有挑战性。

  • Metrics:

我们重复每个实验8次,并报告中位数指标,除非另有说明。在训练/测试曲线中,我们在阴影区域显示了整个最小/最大范围。工作负载运行时间被定义为每个查询延迟的总和。在报告规范化运行时间时,它们是根据专家优化器的运行时间计算的。

8.2 Balsa Performance

我们从端到端的结果开始,回答以下问题:

  1. Balsa在训练和测试查询上的性能如何?

  2. Balsa分别需要多少小时(和执行多少次)来超越专家优化器性能并达到其峰值性能?

  • Performance

图6总结了Balsa的总体性能。在所有工作负载上,Balsa都能够从最小成本模型开始,并学会以相当大的幅度超越专家优化器。

  • Runtime of simulation learning

表2显示模拟数据丰富,耗时数十分钟。因为它只占实际执行学习时间的一小部分,所以我们接下来关注后者。

  • Learning efficiency

图7显示了Balsa的训练性能,它是运行的时间和执行的不同查询计划数量的函数。(后者在RL术语中被称为数据/样本效率,因为每次执行都是与环境的交互。)

  1. Wall-clock efficiency:图7a显示了Balsa在实际执行阶段的挂钟效率。

  2. Data efficiency:图7b显示了数据效率曲线。需要几千次执行才能达到专家级的性能。

  3. Non-parallel training wall-clock:图8显示了每次运行使用一个执行节点的非并行训练时间。在所有情况下,峰值性能在个位数小时内达到,这是一个舒适的“夜间维护”范围。匹配专家的时间最多比并行模式慢3个小时。

  • Sources of speedup

图9显示了Balsa相对于PostgreSQL计划的每个查询加速。

  • Summary

Balsa可以从最低成本模型开始,并学习超越开源和商业专家优化器。Balsa的训练效率很高,需要几个小时来匹配专家,需要数千个计划才能达到最佳表现。

8.3 Analysis of Design Choices

接下来,我们分析了Balsa中各主要组件的设计选择:(1)初始模拟器,(2)超时机制,(3)探索策略,(4)训练方案,(5)波束搜索。

总之,我们发现所有组件都对Balsa的性能和泛化有积极的贡献。

8.3.1 Impact of the initial simulator

Balsa从一个最小的模拟器启动。就先验知识的数量而言,我们可以考虑两种与此选择差异最大的选择:

  1. Expert Simulator:来自专家优化器PostgreSQL的成本模型,它对所有物理算子进行了复杂的建模,并捕获了其执行引擎的细微差别。(注意,这个变体意味着Balsa使用这个成本模型作为模拟器;它不代表PostgreSQL自己的计划。)

  2. Balsa Simulator (§3; C_{out}):一个最小成本模型,它将所有操作的估计结果大小相加。它不了解物理算子或执行引擎。

  3. No simulator:完全跳过引导并从随机权重初始化agent。

图10显示了模拟器的影响。

首先,拥有更多先验知识的模拟器缩短了在训练查询上达到专家性能的时间(图10a)。其次,更多的先验知识也会导致训练结束时的最终表现略好(图10a)。第三,没有模拟的agent(“No sim”)可以完成训练,这是一个惊喜。这是通过使用超时和安全探索来实现的,这保证了大部分学习的安全性。第四,模拟是泛化的必要条件。没有模拟学习的agent在测试时可能会失败(注意图10b中“No sim”的高方差)。

综上所述,从最小模拟器进行自举可以获得良好的训练和测试时间性能。由于新的执行引擎可能没有专家开发的成本模型,因此这种方法具有潜在的推广到新系统和减轻人力开发成本的额外好处。

8.3.2 Impact of the timeout mechanism

我们研究了超时的影响(§4.3),这是一种对实际执行学习至关重要的机制:

  1. Timeout:提前终止执行时间超过当前迭代超时的查询计划。

  2. No timeout:关闭机制。

有了超时,agents就可以在没有希望的计划上节省大量时间,并有可能更快地学习。结果如图11所示。超时agent达到专家性能的速度比无超时agent快35%(图11a)。图11b显示了如何更明智地使用节省的时间:在相同的挂钟时间下,具有超时的agent运行更多的计划,从而加快了学习速度。

总的来说,这些结果表明,超时机制加速了学习,提高了Balsa计划的多样性。

8.3.3 Impact of exploration

探索将RL agents暴露于不同的状态,从而提高性能和泛化。

  1. Count-based exploration (§5):从波束搜索输出中选择最佳未见计划的Balsa安全探索方法。

  2. ϵ-greedy beam search:在每一步的搜索中,以很小的概率ϵ,光束被“坍缩”到一个状态,抛弃了其余的状态。搜索工作仍在照常进行。我们选择了ϵ,使得大约10%的训练查询注入了随机连接。

  3. No exploration:没有使用探索算法。

图12a显示,与其他两种变体相比,具有基于计数的安全探索的agent可以更好地泛化测试查询。更好的泛化是由于经历了更多不同的计划(图12b)。由于空间原因,省略了训练性能,其中基于计数的收敛速度分别比不探索和ϵ-greedy光束快8%和14%左右。

总之,这些结果表明,安全探索是不平凡的,Balsa的基于计数的方法既简单又有效。

8.3.4 Impact of the training scheme

我们将Balsa的on-policy learning与先前工作中使用的完整再训练方案进行了比较,Neo [17]:

  1. On-policy learning (§4.1):Balsa的训练方案使用最新迭代的数据来更新V_{real}

  2. Retrain:在每次迭代中重新初始化V_{real} 并重新训练整个经验(D_{real})。最后一次迭代的V_{real} 被丢弃。

On-policy learning显著加速了训练,比再训练agent快2.1倍(图13a)。更快的学习是由于在恒定大小的数据集上更新V_{real},而不是在越来越大的数据集上重新训练它,从而节省了时间。节省下来的时间用于探索,即执行更独特的计划(图13b)。

8.3.5 Impact of planning time

Balsa使用值网络执行波束大小为b的波束搜索,生成k个完整的查询计划,然后选择执行的最佳计划(在训练期间,选择最佳的未探索计划)。图14研究了Balsa在训练检查点上使用b和k的各种组合的JOB测试查询的计划时间和性能。

8.4 Comparison with Learning from Expert Demonstrations

我们将Balsa与Neo[17]进行比较。本实验使用与§8.3 (PostgreSQL上的JOB负载)相同的设置。由于Neo不是开源的,我们实现了我们最大努力的复制,标记为“Neo-impl”。

图15a显示了训练性能。在初始化时,Balsa比Neo-impl快5倍,因为模拟学习提供了高状态覆盖率(表2),而不是有限数量的专家演示(每个查询一个完整的计划)。Balsa在整个训练过程中保持稳定,因为它采用了超时。

尽管通过5个小时的学习达到了相对稳定的训练性能,Neo-impl仍然不够健壮,无法推广到未见过的测试查询,并且受到高方差的影响(图15b)。

相比之下,Balsa要健壮得多。对于看不见的查询,Balsa始终比专家优化器生成更快的计划,最大加速为2倍。Balsa更好的概括是由于模拟、策略学习和安全探索提供了更广泛的状态覆盖(参见图12和图13)。

总之,Balsa的学习速度更快,执行更安全,由于模拟和探索,它的泛化能力更好,反驳了之前认为需要专家演示的观点[17]。

8.4.1 Comparison with Bao

表3显示,Balsa通常与Bao相同或优于Bao。这些结果并不令人惊讶:它们证实了Bao论文中的发现,即在稳定的工作负载上,具有更高自由度(动作空间)的学习优化器可以在计划质量上优于Bao。

8.5 Enhancing Generalization

图6已经显示,Balsa可以很好地泛化到看不见的测试查询,在没有看到测试查询的情况下,表现优于专家优化器。

在这里,我们研究(i)多样化经验的好处(§6),以及(ii)推广到完全不同的连接模板/过滤器。

  • Diversified experiences

图16显示了中值性能:我们观察到Balsa-8x在几乎所有情况下都提高了训练和测试查询的速度,有时甚至提高了60-80% (JOB Slow, test)。

提高训练速度并不奇怪:重新训练的agent可以混合和匹配基础agent找到的最佳计划。重要的是,测试查询在没有被执行的情况下也看到了很大的加速(例如,在两个引擎上,两个JOB拆分现在都有 >2倍 的测试加速)。这是因为多样化的经验具有高度多样化的计划,因此可以在上面训练更一般化的价值网络。

  • Queries with entirely new join templates

图17a显示了Neo-Impl和Balsa在Ext-JOB上以全部113个JOB查询作为训练集的测试性能。虽然Balsa比Neo-impl更稳定,但在Ext-JOB测试集上,他们都没有超过专家优化器(尽管他们很接近)。这证实了Ext-JOB是一个极具挑战性的泛化工作负载。

接下来,我们将上面描述的Balsa-8x与仅对一个agent的数据进行再训练的Balsa-1x进行比较。令人惊讶的是,在iteration 0中,Balsa8x已经匹配了测试集上的专家优化器(图17b)。这些结果表明,多样化的经验和进一步的探索是提高对分布外查询泛化的有价值的策略。

8.6 Behaviors Learned by Balsa

为了获得对Balsa学习的行为的直觉,我们在训练过程中可视化算子和agent生成的计划的形状组成。结果如图18所示。

与专家优化器相比,Balsa的偏好是新颖的,这种差异在平面形状上尤为明显。这是由于专家优化器是一刀切的,而Balsa则学习根据给定的工作负载和硬件进行定制。

相关工作

  • Learned query optimizers

Balsa与DQ[13]、Neo[17]关系最为密切。DQ提供了这样的见解:查询优化的经典组件——成本估计和计划枚举——可以转换为长期价值估计和规划。所有这三个工作都通过使用学习价值网络和计划搜索来遵循这个公式。在我们的模拟学习中,Balsa还采用了DQ在成本模型之上使用的批处理数据收集。与DQ不同,Balsa演示了在实际执行中对整个工作负载进行微调。

Neo需要从专家演示(PostgreSQL计划)中学习,然后进行微调。相反,Balsa不会向专业的优化器学习。解除这一限制性假设为在未来环境中自动学习优化提供了可能性。

SkinnerDB[31]是一种执行算法,它通过在查询执行期间尝试许多左深连接顺序来学习。Balsa和SkinnerDB都使用超时来缓解糟糕的计划,但提出了截然不同的超时策略。虽然SkinnerDB必须迭代一组与先前执行无关的预定义超时,但Balsa直接使用过去计划的延迟作为超时。Balsa还提供了更通用的功能,因为它可以构建密集的计划和分配物理算子,这两种功能在SkinnerDB中都不受支持。

  • Optimizer assistants

最近的许多方案使用ML来辅助或改进现有的优化器。由于Leis等人[14]表明不准确的基数估计是导致糟糕计划的主要原因,许多项目已经使用ML来改进基数估计[8,10,12,25,28,35,37 - 39],从而帮助今天的优化器找到更好的计划。最近的工作Bao[16]也通过学习为每个查询设置哪些优化器标志来帮助专家优化器。与此工作不同的是,Balsa不帮助现有的优化器,并且在没有专家优化器的情况下精确地处理学习优化。

  • Sim-to-real, timeouts, and caching

模拟到真实、超时和缓存是适用于一系列系统问题的通用技术。Hilprecht等[9]提出使用模拟到真实的方法来学习高质量的数据分区,并使用超时和缓存来优化训练。Balsa将这些方法应用于学习查询优化中,并提出了模拟学习提高泛化的新发现。

经验教训和讨论

在开发Balsa的过程中,我们吸取了一些教训。我们将在下面讨论它们。

  • Simulation learning boosts generalization

令我们惊讶的是,虽然Balsa很好地概括了未见过的查询,但我们发现没有模拟阶段的agents——包括那些从专家演示中学习的agents——在新的查询上变得不稳定(§8.3.1,§8.4)。乍一看,模拟提高泛化的原因可能是违反直觉的。毕竟,我们使用的模拟器是一个最小的、逻辑上唯一的成本模型,它与执行环境无关。它向agent传递了必须纠正的不准确的知识。

我们认为原因是模拟可以使Balsa实现对平面空间的高覆盖。在启动期间,Balsa在每个查询上训练数千个计划(表2),比在实际执行中收集的经验要多得多。然后,在实际执行中,自引导agent可以更新其信念,同时纠正大部分模拟知识,从而提高泛化能力。相比之下,只从实际执行中学习的agent只能看到一小部分查询计划,这可能导致过拟合。

  • Using inaccurate cardinality estimates

在传统的优化器中,基数估计是非常不准确的[14],这可能导致糟糕的计划。然而,在Balsa中,我们发现了对不准确估计的有效使用:在模拟器中使用它们。我们发现不准确的估计仍然可以提供有效的模拟。重要的是,Balsa的性能并没有过度依赖于模拟器——大多数学习都发生在模拟之后,当Balsa使用真实的执行来大大提高模拟的知识(例如,在图7中,初始和最终的性能有4 - 40倍的差距)。与先前的工作[14]一致,我们期望更好的估计会导致更好的模拟器,这将加速学习(例如,“Expert Sim”在图10中)。

  • How to better leverage an expert optimizer, if available?

对于在新系统中学习优化,即使存在兼容的专家优化器(即专家的所有算子都得到目标引擎的支持),先验技术[17]也只建议从专家优化器的计划中进行引导。我们表明,由于演示量有限,这可能导致较差的泛化(§8.4)。相反,Balsa可以通过从专家优化器的成本模型(一个数据丰富的模拟器)中引导来更好地利用专家(参见图10中的“expert Sim”Balsa变体)。我们表明,从成本模型中引导可以显著提高对新查询的泛化(§8.3.1),这是本文的一个新发现。

总结

据我们所知,Balsa是第一个证明在没有专家演示的情况下学习优化器既可能又有效的方法。Balsa通过迭代地规划一组给定的查询,执行它们,并从它们的延迟中学习,以便在将来构建更好的执行计划。为了使学习切实可行,Balsa必须避免可能严重阻碍学习的灾难性计划。我们用三种简单的技术来解决这个关键的挑战:从模拟器引导,安全执行和安全探索。

Balsa为自动学习针对工作负载和计算环境量身定制的查询优化器铺平了道路。新的数据系统可能具有超出我们查询优化知识范围的执行模型[20]或目标[19]。通过自己学习而不是向专家系统学习,Balsa可以为尚未开发的系统减轻显著的优化器开发成本。Balsa是朝着这个令人兴奋的方向迈出的第一步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值