- 直接从 5 个表里选一个开始(比如 A)
- 计算每一个与 A 的联接(A 作为内关系或外关系)
- 发现 “A JOIN B” 成本最低
- 计算每一个与 “A JOIN B” 的结果联接的成本(“A JOIN B” 作为内关系或外关系)
- 发现 “(A JOIN B) JOIN C” 成本最低
- 计算每一个与 “(A JOIN B) JOIN C” 的结果联接的成本 ……
- 最后确定执行计划 “( ( (A JOIN B) JOIN C) JOIN D ) JOIN E )”
顺便说一句,这个算法有个名字,叫『最近邻居算法』 。
抛开细节不谈,只需一个良好的模型和一个 N*log(N) 复杂度的排序,问题就轻松解决了 。这个算法的复杂度是 O(N*log(N)),对比一下完全动态编程的 O(3^N) 。如果你有个20个联接的大型查询,这意味着 26 vs 3,486,784,401,天壤之别!
这个算法的问题是,我们做的假设是:找到 2 个表的最佳联接方法,保留这个联接结果,再联接下一个表,就能得到最低的成本 。但是:
- 即使在 A, B, C 之间,A JOIN B 可得最低成本
- (A JOIN C) JOIN B 也许比 (A JOIN B) JOIN C 更好 。
其他算法
[ 如果你已经受够了算法话题,就直接跳到下一部分 。这部分对文章余下的内容不重要 。]【我也很想把这段跳过去 -_- 】
很多计算机科学研究者热衷于寻找最佳的执行计划,他们经常为特定问题或模式探寻更好的解决方案,比如:
- 如果查询是星型联接(一种多联接查询),某些数据库使用一种特定的算法 。
- 如果查询是并行的,某些数据库使用一种特定的算法 。……
比如,基因算法就是一种:
- 一个方法代表一种可能的完整查询计划
- 每一步保留了 P 个方法(即计划),而不是一个 。
- 0) P 个计划随机创建
- 1) 成本最低的计划才会保留
- 2) 这些最佳计划混合在一起产生 P 个新的计划
- 3) 一些新的计划被随机改写
- 4) 1,2,3步重复 T 次
- 5) 然后在最后一次循环,从 P 个计划里得到最佳计划 。
这是魔术?不,这是自然法则:适者生存!
PostgreSQL 实现了基因算法,但我并没有发现它是不是默认使用这种算法的 。
数据库中还使用了其它启发式算法,像『模拟退火算法(Simulated Annealing)』、『交互式改良算法(Iterative Improvement)』、『双阶段优化算法(Two-Phase Optimization)』…..不过,我不知道这些算法当前是否在企业级数据库应用了,还是仅仅用在研究型数据库 。
如果想进一步了解,这篇研究文章介绍两个更多可能的算法《数据库查询优化中联接排序问题的算法综述》,你可以去阅读一下 。
真实的优化器
[ 这段不重要,可以跳过 ]
然而,所有上述罗里罗嗦的都非常理论化,我是个开发者而不是研究者,我喜欢具体的例子 。
我们来看看 SQLite 优化器 是怎么工作的 。这是个轻量化数据库,它使用一种简单优化器,基于带有附加规则的贪婪算法,来限制可能性的数量 。
- SQLite 在有 CROSS JOIN 操作符时从不给表重新排序
- 使用嵌套联接
- 外联接始终按顺序评估
- ……
- 3.8.0之前的版本使用『最近邻居』贪婪算法来搜寻最佳查询计划
等等……我们见过这个算法!真是巧哈! - 从3.8.0版本(发布于2015年)开始,SQLite使用『N最近邻居』贪婪算法来搜寻最佳查询计划
看过官方文档后,我们了解到 DB2 优化器可以让你使用 7 种级别的优化:
- 对联接使用贪婪算法
- 0 – 最小优化,使用索引扫描和嵌套循环联接,避免一些查询重写 1 – 低级优化 2 – 完全优化
推荐阅读
- 如何挑选黑豆
- 不是夫妻合租房犯罪吗
- 医保断缴三个月余额清零?员工可自愿放弃社保?这些谣言别再信了
- 手串颗数大有讲究,千万别戴错了
- “禁止长时间停车”,到底指的是几分钟?交警:最后再说一遍
- 太热了!别再披头散发了,这4款发型够美够清凉
- 没钱还信用卡有什么解决办法
- 信用卡过期卡怎么处理
- 信用卡逾期被注销怎么还钱
- 如何找回抖音私聊记录