首先要考虑一下多表连接时可能产生出多少种连接顺序:
- 对于两表连接,比如表 A 和表 B 连接只有 AB、BA 这两种连接顺序。其实相当于 2 × 1 = 2 种连接顺序。
- 对于三表连接,比如表 A、表 B、表 C 进行连接
- 有 ABC、ACB、BAC、BCA、CAB、CBA 这么 6 种连接顺序。其实相当于 3 ×1 × 1 = 6 种连接顺序。
- 对于四表连接的话,则会有 4 × 3 × 2 × 1 = 24 种连接顺序。
- 对于 n 表连接的话,则有 n × (n-1) × (n-2) × ··· × 1 种连接顺序, 就是 n 的阶乘种连接顺序,也就是 n!。
有 n 个表进行连接,MySQL 查询优化器要每一种连接顺序的成本都计算一遍么?那可是 n!种连接顺序呀。其实真的是要都算一遍,不过 MySQL 用了很多办法减少计算非常多种连接顺序的成本的方法:
提前结束某种顺序的成本评估
MySQL 在计算各种链接顺序的成本之前,会维护一个全局的变量,这个变量表示当前最小的连接查询成本。如果在分析某个连接顺序的成本时,该成本已经超过当前最小的连接查询成本,那就压根儿不对该连接顺序继续往下分析了。比方说 A、B、C 三个表进行连接,已经得到连接顺序 ABC 是当前的最小连接成本,
比方说 10.0,在计算连接顺序 BCA 时,发现 B 和 C 的连接成本就已经大于 10.0时,就不再继续往后分析 BCA 这个连接顺序的成本了。
系统变量 optimizer_search_depth
为了防止无穷无尽的分析各种连接顺序的成本,MySQL 提出了optimizer_search_depth 系统变量,如果连接表的个数小于该值,那么就继续穷举分析每一种连接顺序的成本,否则只对与 optimizer_search_depth 值相同数量的表进行穷举分析。很显然,该值越大,成本分析的越精确,越容易得到好的执行计划,但是消耗的时间也就越长,否则得到不是很好的执行计划,但可以省掉很多分析连接成本的时间。
根据某些规则压根儿就不考虑某些连接顺序
即使是有上边两条规则的限制,但是分析多个表不同连接顺序成本花费的时间还是会很长,所以 MySQL 干脆提出了一些所谓的启发式规则(就是根据以往经验指定的一些规则),凡是不满足这些规则的连接顺序压根儿就不分析,这样可以极大的减少需要分析的连接顺序的数量,但是也可能造成错失最优的执行计划。他们提供了一个系统变量 optimizer_prune_level 来控制到底是不是用这些启发式规则。