这是我参与「第四届青训营 」笔记创作活动的的第1天
傲慢原罪
在此我觉得应该批判一下某些程序员的傲慢,他们认为自己对SQL已经熟悉得不能再熟悉了,增删查改,建库建表,甚至索引优化等等都能拿出手。
但是让我们回到SQL的起点:什么是SQL?现在我提出SQL中最简单的一个问题,请问SQL是哪三个英语单词的缩写,它的中文全称是什么?
这个问题至少能够让30%以上的程序员语塞,这些程序员或许CURD快得出神入化,对SQL调优讲得那是口若悬河。在此我并不是想嘲笑谁,也不是想要贬低谁,但是当你走到高处的时候,也请好好的思考一下自己的基石是否已经摇摇欲坠。
SQL -Optimizer
WHAT
首先,我们需要知道什么是 SQL-Optimizer。
stuctured query language optimizer 可以译作:结构化查询语言的优化器
正如其名,这东西就是为了去优化查询语句的执行效率而存在的,一个优秀的optimizer可以让sql查询效率提升数十倍,使得在大体量的数据下,查询成本降低,服务器压力下降,用户体验提高。
WHY
为什么我们需要去开发 SQL-Optimizer?
虽然我原生SQL查询效率低下,但是已经能够满足我的需求,以及用户的需求了,我凭什么要开发这玩意?这玩意开发需要多高的成本?我们真的能够切实寻找到一个高效率的优化顺序吗?
这当然是绝大部分小型厂家遇到的疑惑,以及部分学习者同样内心底部有这么一项怀疑。无疑这优化需求是真真实实要在大体量数据中才会遇到的。假设我家公司规模非常大,某些数据库达到百T的储存,我查询一次就得花上几分钟,那用户还用不用?SQL优化器,正是为了解决这样的问题而存在的。(也就是说,不要盲目的使用优化器,在此之前可以为自己的数据体量做一个简要的预估)(至少除去中间件可以使得代码可维护性也会提高,代码更加健壮)
HOW
那么,SQL-Optimizer是如何做到查询优化的呢?不急,我们首先得知道SQL语句的简要原理,再来说说优化器是如何作用于其上的
SQL的执行
sql语句很明显是一个string类型的字符串
- 我们首先将他交给parse(语法分析器),将其处理为一个抽象语法树(大一做过计算器的应该不陌生吧)
- 再将结构树交给analyzer(分析器),将其处理为一个logical-plan(逻辑计划),大概你可以理解为给树的执行流程做成了一个有序列表(我们就依次执行sql底层的原语就能得到我们最终需要的结果)
- 之后将执行的顺序列表交给optimizer(优化器,本节重点内容),由优化器去判断和调整执行顺序,使得真实执行语句时更加迅速,减少服务器的压力,生成一个physical-plan(物理计划)
- 最终将物理执行计划交给executor(执行器),执行并得到数据
优化方案
优化方案大体可以分为两大类:
一种是按照遍历抽象语法树的顺序来划分:
-
Top-down 由上而下遍历树,找到完整的最优执行计划
- volcano/cascade, sqlserver
-
Buttom-up 由下而上遍历树,找到最优执行计划
- system R, postgreSQL, IBM DB2
另一种是按照优化方法去划分:
- RBO 通过规则去优化,通过关系代数的等价语义去重写查询
- CBO 通过模型估算执行代价的计划,根据估算结果去选择最终的执行语句
这里需要注意的是,这些优化方案不是顾此失彼,可以将之结合起来按照权重划分来进行混合使用(大致就是生成一个决策树或者α-β剪枝之类的算权重)
RBO
RBO全称是Rule Based Optimizer,基于规则的优化器。那正如上面所说通过规则去优化,通过关系代数的等价语义去重写查询
RBO的优化思路:
- 列裁剪:在扫表的时候就把没用的列数据去掉,减少内存和CPU压力
- 谓词下推:把一些where限制语句在连表之前执行(正常的原生语句顺序是:扫表-连表-谓词过滤-输出)
- 传递闭包:这里需要程序有部分的语法分析能力,比如where语句中对A表id做了限制,join语句的内连条件也是id,那么这意味着同时也对B表的id做了限制,将AB表的id限制语句同时在连表之前执行,就能大大降低连表时的压力
- Runtime Filter:运行时过滤,这玩意我也不咋懂,大致就是连表最初两表操作时并行的,在A表处理完数据后,可以将自己的数据特征传递给B表,B根据这个特征先对自己数据进行一次筛选,再进行自己的数据处理,最后再连表
CBO
CBO全称是Cost Based Optimization,基于成本的优化器。通过模型估算执行代价的计划,根据估算结果去选择最终的执行语句
CBO中重要的两个统计参数分别是
- selectivity 选择率
- cardinality 基数
- 明确的基数,远比代价模型本身更为重要
CBO的执行思路:
-
新建一个表储存模型信息以及基数信息
-
接下来进行数据收集,又分为以下三种思路
- 每次数据库进行增删改,都触发trigger采集数据
- 每隔一段时间手动执行采集
- 动态采样,采集部分信息作为样本进行数据推导
-
对sql语句中的每一种语句做一个概率和性能的估算(那么这些语句组按照顺序组合在一起就会得到一个值)
-
然后进行等价重排后再次计算这个值,得到代价最低的(这里应该也会使用到决策树剪枝算法类似的东西)(spark官网里面貌似有类似的例子,可以去瞅瞅)
其他
剩下的就是一些实际优化器的实现方案,这就不一一列举了,累死个人
结语
这是我第一次做笔记,会比较详细一点,并且前面用大篇幅去阐述了一些和课题不太相关的东西,原因是我认为做好某样东西,一定要了解他的起因,经过和结果。相信很多人和我一样,很多时候并不是真的没法了解某样东西,而是会去思考:
"为什么它要做这个?"
以及
"它为什么要这样做?"
这样的问题,这类似的问题有时候你需要去了解他的历史起源,有的时候这样的问题根本无解。
但是真正了解它干了个什么事之后,你就至少可以做到"不惑"和"不惧",即便别人真的做得很好,你的反应也是"啊,这家伙这算法真厉害,我怎么没想到",而不是"别人都做出来了,我还不知道这东西到底是什么,算了学会用就行了"