介绍
如题目所示,这是一篇介绍in常量查问的源码解读文章,但又不限于in常量查问,因为其中波及的很多设计与优化对于大多数查问都是普适的。 一如平常一样,咱们首先会过一遍整体的执行流程,梳理一个大抵的框架。紧接着,同时也是更重要的,咱们会通过一系列在实在场景中遇到的问题(说白了就是性能优化),来对各种细节解决进行加强。
舒适揭示:倡议有条件有趣味的同学能够对照着本篇文章边调试(我基本上把重要的断点地位都截了图)边学习边思考,这样印象和了解应该会更加粗浅。
心愿大家在读完之后,能够尝试着答复以下一些问题来进行某种测验:
- 什么是分片裁剪?为什么要进行分片裁剪?
- 为什么要对物理SQL中值进行裁剪?
- 什么是plan cache?为什么须要?
- 为什么须要post planner ?
- XPlan是什么?为什么Xplan比物理SQL更优?
- 为什么要有一个ToDrdsRelVisitor?
- 什么是全局二级索引?如何利用?
- 以及其余散落于文章中或者浏览时的问题
从大抵的流程说起
注:具体的执行流程请参考文章,https://zhuanlan.zhihu.com/p/...,咱们这里只介绍其中几个比拟重要的环节。 咱们拿一个非常简单的场景来看一下吧,一个简略的表如下,create table t(c1 int, c2 int, c3 int) dbpartition by hash(c1) tbpartition by hash(c1) tbpartitions 2,一条最简略的SQL如下:select c3 from t where c1 in (1,2)。挑了五个阶段进行了并不太详尽的阐明,如果你感觉比拟形象时,也能够入手调试一下,一些概念应该就会更加清晰了。
阶段一
咱们须要将SQL文本解析为语法树,如果不非法,则报错,要害断点如下图,其中sql为输出的查问语句,statement为通过解析后的语法树。
须要留神的是,在这个中央,咱们是只进行语法解析,而不进行语义解析。什么意思呢,比方你当初输出的SQL为select c1 from tt,此时尽管咱们没有tt这张表,然而断点处还是会失常解析出一个SQLSelectStatement,有趣味的同学能够打个断点试一下。
阶段二
如上剖析,咱们当初要进行语义的校验了,比方我怎么晓得这张表存不存在,以及是否含有这个列呢?
阶段三
构建执行打算,在toRel时将由 SqlNode 形成的 AST 转换为由 RelNode 组成的逻辑打算。
埋一个坑把,有趣味的同学能够联合代码思考一下,既然咱们曾经拿到了逻辑执行打算,那么ToDrdsRelVisitor的作用是什么呢?
阶段四
对执行打算进行优化,以期取得较为优异的执行成果。
阶段五
拿到执行打算之后,紧接着咱们来看一下是在哪里执行的,以及是如何执行的
咱们能够简略看一下这个plan,这是一个非常简单的plan,最上层是一个Gather用来聚合上层多个logicalView的后果,而logicalView中蕴含了如何与存储节点进行交互的信息。 依据plan拿到相应的handler,而后进行调用就能够了。
在这个场景中,咱们会递归调用logicalView的handler。
OK,以上就是一个大略的执行流程,接下来咱们来真正深刻到一些细节看一下,咱们如何将这个大抵的流程进行丰盛以使其可能满足工业生产的需要。
事实中的应用场景
In查问列表中的值不固定,个数亦不固定。
优化思路
单条SQL的优化,比方分片裁剪,物理SQL中in值的裁剪,应用XPlan代替物理SQL。
大量执行类似的SQL时,防止重复性且不必要的工作,如防止每次从新生成plan。
对其中一些非凡场景进行更加极致的优化,比方单分片间接下推。
通过增加索引进行优化,在这里咱们次要探讨全局二级索引。
具体的优化
单条SQL的优化
分片裁剪:只拜访必须拜访的分片
Q:select from t where c1 in (1,2) 会向所有分片下发物理SQL么?
A:不会的。通过下面的剖析,咱们下发的物理SQL为select from t_physical_table where c1 in (1,2),t_physical_table为逻辑表t所对应的物理表。而因为表t的分库键和分表键均为c1,因而显然咱们只须要向两张可能存在匹配记录的物理表下发物理SQL即可,获取裁剪后的分表信息如下图。
分片裁剪是肯定须要调用分片计算,分片计算的逻辑在这里。
物理SQL中in值的裁剪:只留下有用的in值
Q:下发的物理SQL中,是否会对in的列表进行裁剪呢?
A:会的,而且对下发的物理SQL中的in列表中的值进行裁剪,次要有两个益处,一是尽可能防止下发的物理SQL导致不必要的全表扫描,二是缩小下发物理SQL的长度。
上图中PruneRaw即代表裁剪后的in查问列表。
应用XPlan代替物理SQL:防止DN节点进行物理SQL的解析优化
注:详情可参考链接文章中的执行打算传输局部,https://zhuanlan.zhihu.com/p/...,%E8%BF%9B%E8%A1%8C%E4%BA%86%E7%89%B9%E6%AE%8A%E4%BC%98%E5%8C%96%E3%80%82 Notice:in查问其实临时是不反对传输执行打算的。 但我感觉可能没什么特地非凡的中央,像传输其余的plan一样,咱们须要在计算层指定数据的拜访形式(即指定索引),而后进行适配和对接。
防止每次从新构建plan
防止参数值不同而重复构建plan
Q:每次都进行plan的构建,看起来并不是十分有必要,比方select from t where id in (1,2) 和select from t where id in (2,3)。
A: 是的,所以咱们对plan进行了缓存,这就是PlanCache组件,能够了解为Map。很天然的,咱们须要对上述两条SQL进行参数化以便从map中进行查找,即参数化为select * from t where id in (?,?)的模式,代码在
防止参数个数不同而重复构建plan
Q:仔细的同学可能感觉有点奇怪,下面的select c1,c3 from t where c1 in (1,2) 参数化后为 select c1,c3 from t where c1 in (?),而非select c1,c3 from t where c1 in (?,?),这是为什么?
A:这样做是为了防止plan cache的收缩,因为这样参数化之后,select c1,c3 from t where c1 in (1,2) 和select c1,c3 from t where c1 in (1,2,3,4)就是共用一个plan cache了;此外,这样还能够缩小参数化SQL占用的内存,设想一下,有些SQL中in列表中的值多达几十万个呢。
单分片场景优化
Q:对于某些场景,是否有更近一步的优化,毕竟TP是须要尽可能的高性能的。
A:有的,比方单分片的场景,in列表中的值会落在同一个物理分表上。 咱们能够思考下此时上面的执行打算是否能够简化?
答案是显然的,在单分片场景下,下层的Gather是齐全不须要的,否则咱们在执行时会有额定的执行开销。 引申:咱们能够再联合后面的参数化与plan cache来了解这个问题,即参数不同的SQL的最优执行打算其实并非总是雷同的,但咱们为了防止每次反复生成plan,又会缓存一个plan,于是咱们须要一个可能对plan进行优化的能力。 咱们大略能够把这种状况分成两种,一种是参数不同导致抉择的join算法不同,比方是抉择bka join还是hash join,为了解决这个问题,咱们引入了执行打算治理模块(SPM);另一种则跟咱们的架构有十分大的关系,因为咱们上层的DN(能够简略了解为mysql)显然是具备执行各种SQL的能力的,而如果在某些参数下,通过裁剪后只剩下一个分片了,则该SQL通过物理表名的替换后可间接下发到DN执行,计算层只须要期待后果返回即可,无需做任何其余的操作。为了实现第二种成果,咱们在planner阶段减少了一个阶段,叫做post planner,在post planner中会判断是否可能下推到某个分片,默认为关上,上图中为了演示须要,特意应用hint进行了敞开。
增加全局二级索引
注:索引,实质是一种批改与查问的衡量,须要用户审慎思考,尤其写入全局索引会带来较大的分布式事务开销。
Q:分片建曾经确定了,in查问的字段没有跟分片对齐,是不是无奈做分片裁剪了,还能优化么?
A:能够思考减少全局二级索引。 咱们来举个例子吧,比方table: t3(c1 int, c2 int, c3 int) dbpartition by hash(c1); SQL为select c3 from t3 where c2 in (1,2),由执行打算可知咱们无奈进行分片裁剪,因而须要拜访所有8个分片,如下:
当初让咱们来考虑一下如何优化? 咱们的目标是心愿缩小拜访的分片数,而之所以无奈进行分片的裁剪,是因为in查问的字段和分片键没有对齐。于是解决方案也很简略,咱们减少一个拆分键与in查问字段对齐的全局的二级索引即可,无关全局二级索引的介绍,可参考链接,https://help.aliyun.com/docum...。 比方,咱们执行如下增加全局二级索引的SQL,alter table t3 add global index g_c2(c2) covering(c1, c3) dbpartition by hash(c2),而后咱们再来看下此时的执行打算,发现此时曾经如咱们所料进行了基于全局二级索引的分片裁剪,当初只须要扫描两个分片即可。
一个小练习
In列表中蕴含大量反复值时,能够如何优化?(咱们当初的版本没有思考这种状况) 比方,有一个很简略的做法,在参数化时加一个去重,如下图。
而后大家能够思考一下,须要留神什么,以及有什么问题么?
One More:横向比照与思考
大家有趣味,有工夫的,能够比照其余友商数据库进行比拟与剖析。
总结
其实我在这篇文章外面,抛了挺多问题,有些给了一种便于叙述却未必全面的答案,有些则齐全没有答复。最初的这个总结我感觉也留给大家来写了。
原文链接
本文为阿里云原创内容,未经容许不得转载。