TiDB SQL Engine Team:纯手工打磨前沿的优化器和执行引擎|PingCAP 招聘季

Easter79
• 阅读 585

“SQL at SCALE”(出自 PingCAP 官网)是我们对 TiDB 的一个精简概括,而我们 TiDB SQL Engine Team 正是负责这 3 个单词中的 “SQL” 部分,其重要性可见一斑。SQL 在数据库中的大致处理流程可以简短概括为查询优化和执行,这期间涉及到 SQL Parser、优化器、统计信息和执行引擎等模块,他们就是 TiDB SQL Engine Team 目前所负责的模块。接下来我会用简短的篇幅向大家介绍 SQL Engine 的背景知识,以及我们在做的事情,面临的挑战等。

关于查询优化

优化器是 SQL 引擎的大脑,负责查询优化。查询优化的主要工作概括起来很简单:搜索可行的执行计划,从中挑一个最好的。但要做好这两件事却是整个分布式数据库中最难的地方。

1979 年 Selinger 发布了 “Access Path Selection in a Relational Database Management System”,正式拉开了 Cost Based Optimization 的帷幕,这篇论文也被视为 CBO 优化器的圣经。在这之后陆续出现了 Starburst(1988 年),Volcano Optimizer Generator(1993 年)和 Cascades Framework(1995 年) 等,每年数据库三大顶会中也能看到不少查询优化相关的论文,整个优化器领域可谓是蓬勃发展。但即使如此,优化器也仍然有很多问题未能得到很好的解决,比如:

  1. Guy Lohman 2014 年在 “Is Query Optimization a “Solved” Problem?” 中详细讲述的 SQL 算子结果集估算的难题。简单来说,要估算某个表需要扫多少行数据比较容易,但是要再估算更上层的 SQL 算子,比如 Join 或者 Join 之后再 Group By 的结果集有多大,这个就很难了。可以想象的是,估算误差会随着层数的增加而被放大,这个放大有时候是数量级的。此外还会出现负负得正的情况:明明估算错了,但是执行计划却是对的,纠正估算误差后,执行计划反而不对了 🤷‍。

  2. Viktor Leis 等人在 2015 年的论文 How Good Are Query Optimizers, Really? 中讨论了优化器的另一朵乌云:Join Order。如果枚举所有可行的 Join Order,光是考虑左深树,N 个表的 Join 就可能有 N! 种执行计划。目前大家普遍采用一种妥协的方案:当参与 Join 的表比较少时用动态规划来确定 Join 的顺序,表比较多的时候用贪心或者遗传算法(PG 用的模拟退火)来做。但是采用什么样的动态规划和搜索算法也仍然处在热烈的研究中,而算子结果集的估算误差又进一步让这个问题雪上加霜,难上加难。

作为一个从头到尾完全自己手写的优化器,TiDB 优化器的发展历史也算精彩:一开始我们是 Selinger 的 System R 模型,但是它的扩展性不是很好,搜索空间有限,维护成本也高,于是我们调研后,决定开发 Cascades 模型的新优化器。具体请参考:十分钟成为Contributor 系列| 为Cascades Planner 添加优化规则揭秘TiDB 新优化器:Cascades Planner 原理解析。在开发 Cascades Planner 的同时,我们还在做着另外一件非常重要的事情,提升优化器的稳定性:

  1. 优化器的稳定性非常重要。去年之前我们经常遇到选错索引,或者干脆不选索引的问题。这个对业务的影响非常大,有时候一个慢查询可能拖垮整个集群,很多用户都吐槽过这个问题。后来调查研究后,我们引入了 Skyline Pruning 的剪枝优化,极大地提升了优化器选择索引的稳定性。参考:Proposal: Support Skyline Pruning

  2. 优化器的稳定性非常重要。要稳定的做出好的执行计划,统计信息非常非常关键。以前我们收集统计信息需要整个表都扫描一遍,扫的过程中用蓄水池算法做抽样。小表这样做没啥问题,大表也这样做就不行了:一方面担心对正在运行的业务造成影响,另一方面这种方式也很低效。于是我们结合 TiKV 的存储特点引入了 Fast Analyze,极大的提升了统计信息的搜集速度,也降低了对业务负载的影响。参考:PR/10214

  3. 优化器的稳定性非常重要。即使我们做了各种优化,解了各种 Bug,仍然会出现执行计划不优的问题。有条件的用户还可以改一改 SQL,那没条件的呢?比如 SQL 是通过第三方工具自动拼接的怎么改?为了解决这些问题,我们决定引入 SQL Plan Management,先实现了给 SQL 绑定执行计划的功能,使得不用更改业务也能抢救 SQL 的执行计划(Issue/8935);为了能够应对更多业务场景,更加细粒度的控制优化行为,我们还丰富了 SQL Hint 集合(Issue/12304);为了让 SQL 执行计划不会变差,我们为 SQL 确定了 Plan 的 Baseline,并且再往前走一步,我们做了 Baseline 的自动演进,使得执行计划不但不会变坏,而且只会变的越来越好。

重要的事情重复 3 遍:优化器的稳定性非常重要。

除了稳定性之外,还有性能问题:

  • 如何在尽量短的时间内消耗尽量少的硬件资源找到最佳执行计划?

  • 而目前 TiDB 正在 HTAP 之路上迈出坚实步伐,如何自动识别一条 SQL 是 AP 还是 TP 查询?

  • 如何为 TP 查询选择合理的索引?

  • 又如何为 AP 查询做出一个高效的分布式执行计划?

可以预见,在这条道路上,优化器又将迎接新的困难和挑战,不断自我演进。

关于查询执行

我的第一份工作从执行引擎开始,对它的感情异常深厚。执行引擎的目标是尽量利用计算资源,正确且快速的完成执行计划所描述的计算任务。光有看起来很完美的执行计划,却没有高效的执行引擎,整个 SQL 引擎也是废的。

执行引擎也是一个热门的研究领域。最经典的执行模型当属 1994 年 Goetz Graefe 发表的 Volcano 迭代器模型,至今仍被广大数据库使用。原因很简单:接口抽象度高,扩展性好,实现起来简单。在数据量不大的 TP 请求中,这种模型足够用了。不过后来大家发现,随着数据量的上升,这玩意的执行性能很差:每完成一条数据的计算,要额外花费的很多 CPU 指令,计算效率非常低。于是有了后来的两大优化方向:Vectorization 和 Compilation,各自的代表分别为:2005 年 Marcin Zukowski 的 ”MonetDB/X100: Hyper-Pipelining Query Execution” 和 2011 年 Thomas Neumann 的 “Efficiently Compiling Efficient Query Plans for Modern Hardware”。

除了执行框架,如何利用 CPU 硬件特性优化各种执行算子也被广泛的讨论和研究。比如 2013 年的《Multi-Core, Main-Memory Joins: Sort vs. Hash Revisited》这篇论文详细的探讨和对比了 Hash Join 和 Merge Join 的实现和性能,2015 年的《Rethinking SIMD Vectorization for In-Memory Databases》这篇论文详细讨论了如何利用 SIMD 指令提升 SQL 算子性能。此外,底层软硬件技术的革新带来更多的优化机会,比如还有一系列论文来讨论如何适配 NUMA 架构,提升算子执行性能等。

作为一个从头到尾完全自己手写的执行引擎,TiDB 执行引擎的发展也非常丰富多彩:一开始我们使用的是传统 Volcano 迭代器模型,后来我们和社区同学在 TiDB 2.0 版本中将其优化成了向量化模型(Issue/5261),得到了巨大的性能提升:TPC-H 50G, TiDB 2.0 VS 1.0。之后我们和社区同学优化了聚合算子,重构了整个聚合函数的执行框架,执行性能又取得了飞跃的发展(Issue/6952)。再之后,我们和社区同学优化了表达式执行框架,使得表达式执行效率得到了 10 倍的性能提升,这期间 10x Performance Improvement for Expression Evaluation Made Possible by Vectorized Execution and the Community 这篇文章还占据了 Hacker News 的首页和 DZone Database 头版头条。

稳定性和易用性也非常重要。为了解决用户 OOM 的问题,我们先后引入了内存追踪和记录的机制,后来干脆让算子落盘真正解决内存使用过多的问题,另外我们也在优化排查问题的调查工具,方便在出问题时快速定位和 workaround。

如前文所说,目前 TiDB 正在 HTAP 之路上迈出坚实的步伐。执行引擎将在新的征程上肩负着新的使命。在分布式数据库中,广义上的执行引擎需要考虑更多的事情:任务如何调度?shuffle 如何优化?目前三套执行引擎(TiDB、TiKV、TiFlash)三套代码的维护成本如何降低?这些问题都等待着我们去探索和解决,可以预见,在这条道路上,执行引擎又将迎接新的困难和挑战,不断自我演进。

期待你的加入

很开心,TiDB 的优化器和执行引擎是从零开始由我们的小伙伴们纯手工打造的,我们有很大的自由度来发挥自己的创造力;很紧张,上面这些列出来的种种问题我们都会遇到;很荣幸,我们能够和业界大牛、广大开源爱好者们一起来攻克这些难题;也很有成就感,我们能在广大 TiDB 用户的业务中看到这些改进为他们带来的价值。

我们热爱开源,相信开源能够为我们的产品带来巨大的收益,也愿意为开源奉献,非常期待同样热爱开源的你的加入。如果你:

  1. 热爱和相信开源,聪明且有激情;

  2. 敢于挑战上面那些难题,突破极限;

  3. 熟悉分布式系统、优化器和执行引擎的实现,熟悉 CPU 硬件特性;

  4. 有团队带领经验(加分项)。

那么我们就加入我们吧,一起向这些难题发起挑战,构建一个前沿、稳定的优化器和高效易用的执行引擎。

加入我们吧!

我们认为优秀的工程师或多或少有以下共同特质:

· A Quick Learner
· A- n Earnest Curiosity
· Faith in Open Source
· Self-driven
· Get Things Done

如果你符合以上特质,欢迎进入招聘页面查看目前开放的工作机会。

简历投递通道:hire@pingcap.com

实习生:公司的各项福利和学习资源对实习生全面开放,更重要的是实习生还未毕业就有机会接触工业级项目,而且实习期间表现优异者将有机会获得校招绿色通道特权。针对实习时间并不充裕的小伙伴,你可以先通过 Talent Plan 丰富基础知识(https://university.pingcap.com/talent-plan/),也可以通过参与 TiDB 开源社区获得更多实践机会!

伯乐推荐:如果你身边有符合以上要求的小伙伴,也可以找我们聊一聊,推荐成功就有机会获得伯乐推荐奖励。伯乐推荐邮件格式:[伯乐推荐] 候选人姓名-职位名称-推荐人姓名-推荐人手机号。

延展阅读

是的,我们在招人!PingCAP 2020 招聘季正式开启

TiDB Architecture Team:挑战数据库的本质难题

揭秘 PingCAP 年轻前沿的团队:用户生态

TiDB SQL Infra Team:一起打造从计算层到存储层的完美桥梁

TiDB SQL Engine Team:纯手工打磨前沿的优化器和执行引擎|PingCAP 招聘季

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k