3.3执行计划的控制前面已经介绍了各种类型的执行计划,相信读者应该对这些执行计划已经有了一个深刻的理解。完全理解了这些执行计划也就意味着完全理解了数据库的内部执行步骤。在深刻地掌握了内部执行步骤的前提下,就应该知道在什么样的情况下使用什么样的方案可以获得最佳的执行效率。深刻理解了原理的人与只知其表不知其里的人相比,从根本上会有所不同。我相信本书中介绍的技术将成为通向数据库管理技巧新境界的捷径。同时我相信各位读者对优化器也已经有了充分的认识,并且相信大家应该已经具备了能够正确判断出某个查询语句的最优执行计划的能力了,以及具备为了让某个查询语句按照最优的执行计划执行而应当采用何种方法的能力。所以希望各位读者能够以此为基础,预先找到比较好的优化因子(数据和索引结构、统计信息等),编写出高质量的SQL语句。这有点类似于现实工作中的领导者,尽管领导们没有直接参与实际业务,但是他们能够在正确把握实际业务的基础上,合理地为每个员工分配与之相适应的工作。如果领导者不能正确地把握实际业务而只知道向其下属下达命令的话,员工们的优势就无法得到最大程度的发挥。作为优秀的领导者,应该具备较强的判断力,根据不同的情况及时地对实际工作做出合理的调整。同理,我们要以对数据库原理的深刻理解为基础,对数据库所做的事情进行必要的合理规划。虽然优化器版本在不断升级,功能也在不断增加,但它仍然存在无法克服的缺陷。对于我们而言,不仅正确评估优化器判断的合理性会比较困难,使用合适的方法引导优化器按照我们的意图去执行同样也不简单。这就好比开车时不仅要使用油门,同时也要使用刹车来控制车辆行驶一样。在本节中要介绍的是根据不同情况控制优化器的方法。方法有两种:其一,使用提示(Hint);其二,直接改变SQL类型。3.3.1提示的活用准则提示(Hint)是将我们的要求传达给优化器的重要手段,提示诞生的初衷是为了弥补优化器的不足之处。这有点类似于为了避免选手的失误而需要教练的指导。相信喜欢高尔夫的人应该都知道著名选手泰格·伍兹,尽管他很出名,但他也是在教练的指导中成长的。虽然泰格·伍兹已经是世界一流选手,但他仍然需要教练的指导。现在教练应该不需要对他的所有训练都进行详细指导,只需要指出他的不足之处即可。同理,早期优化器的问题比较多,所以就需要使用提示来发现并弥补其不足之处;但现在的优化器技术已经得到了很大的提高,只有在非常特殊的情况下,为了引导优化器按照我们期望的方式执行才使用提示。与其说是为了发现其不足之处而使用提示,还不如说是因为我们知道优化器所不知道的信息,为了实现特殊目的而使用它。从另外一个侧面看,提示有点类似于下棋时在旁边做“参谋”的人。参谋类似于军师,其作用就是给别人提供好的意见和建议,但是也只不过是起建议性作用而并不是决定性作用。下棋时棋手也仅仅是参考一下参谋的意见而已。当棋手认为参谋的意见对自己有帮助时才会采纳参谋的意见,否则就会无视参谋的意见而继续按照自己的想法下。而且有时在棋手心情不太好的情况下,不论参谋的意见正确与否都给予全盘否定。优化器类似于棋手,提示类似于旁边的参谋,优化器对于用户给予的提示只是参考而已,心情不好时也会直接给予全盘否定。在作者看来,现在的优化器“自尊心”非常强,经常不愿意接受旁边参谋的意见(提示)。就像棋输了,棋手无法追究参谋的责任一样,那么,如果执行效率低下,能否向编写SQL语句者质问执行计划的责任呢?经常会发生此类现象,即提示分明提供了最优的执行路经,但经常被优化器置之不理。当然对于并不熟悉最优执行计划的人来说,无法识别优化器判断的好坏;可对于非常精通执行计划的人来说,往往能够识别出更多较好的执行计划。有时优化器极力拒绝使用提示的做法会让人感到不知所措,尤其是在复杂查询中到处都使用了提示的情况下,这种现象越发严重。但如果心平气和地反复试验,认真地组合所使用的提示,在大部分情况下,最终都可以引导优化器按照我们所期望的方式来制定执行计划,当然该执行计划肯定也是更合适的执行计划。出现这种现象的原因,与其说是优化器太过于以自我为中心所致,还不如说是优化器使用探索法来选择执行计划的策略所致。该策略就是优化器从接近于最优化或者比较好的执行计划中筛选一定量的执行计划,并对其进行排序,然后从该排序结果中选择一个最好的执行计划。优化器创建执行计划时并不以各个部分所使用的提示为准则,而只在选择良好执行计划时对提示的建议进行参考,所以出现没有按照提示所引导的方式制定执行计划的现象也是很正常的事情。例如,我们在最开始试图操作一个比较棘手的机器时可能会觉得非常困难,但经过一段时间的熟悉,就可以按照自己的意图操纵了。同理,也许在刚刚开始接触提示时你会发现掌握它的使用方法比较困难;但如果坚持努力学习的话,它的使用方法就会变得非常简单。随着版本的不断升级,有可能增加一些新的提示,也有可能使用一些新提示替代一些旧提示,或者删除了一些旧的过时的提示。这里希望读者能够非常熟练地掌握提示的使用方法,因为只要有一种提示存在,就意味着有与其相对应的数据读取路径存在。建议各位读者认真研究每次新出现的提示,因为它的出现意味着DBMS向我们提供了新的数据读取路径。总之,希望读者能够充分地认识到提示的重要性并熟练掌握每个提示的使用方法。同样,如果某个旧的提示被某个新的提示替代了,这也就意味着优化器采用了更好的方法改善了执行计划。某个提示被删除了,也就意味着优化器不再向我们提供与该提示相对应的执行计划。因此,当提示发生变化时,很有必要认真研究一下该变化是否会对正在使用的SQL语句造成影响。在实际工作中,如果在大部分的查询语句中都使用了提示,很明显这有点不太正常。让我们从常理的角度来思考一下,参谋的建议和教练的指导仅仅是偶然为之,如果经常需要他们的话就有点不正常了。如果有10%的查询语句使用了提示,就有必要寻找一下原因,导致提示增加的可能原因有两个:第一,优化器模式的选择上存在问题;第二,没能创建战略性的索引。以前我曾经遇到了一件让人惊讶的事情,某个用户在没有表连接的情况下竟然使用了“ordered”提示。问他为何使用该提示时,他的回答让人更吃惊,他说:“无意中在某个SQL中看到了使用该提示大幅度提高了执行速度,所以我想在这里使用也许也会提高执行速度。”很明显该用户过分地信赖“ordered”提示了。相信各位读者应该不至于此。有一次作者访问一家企业时遇见了一件让人哭笑不得的事情,他们在所有的SQL中都使用了提示“FIRST-ROWS”,向DBA问其缘由,获得的答案与上面的答案相同。让人觉得无奈的是他们连提示的拼写都没有搞清楚就在所有的SQL中强制性地使用了“FIRST_ROWS”提示。还有一家企业自认为已经对SQL进行了充分的优化,几乎在所有的SQL中都使用了各种各样的提示。他们并不是为了引导优化器制定他们所知道的最优执行计划而使用提示,而是尝试性地使用多个提示,然后通过观察其执行计划来选择一个最能提高执行速度的提示。他们所采用的这种方式真不愧是所谓的“反复试验法”。他们之所以这样做主要是因为不了解优化器是按照何种方式制定执行计划的,并且认为执行计划一旦被固定就不会再发生变化了。不必要的提示会对数据访问路径的选择有极坏的影响。在大量使用提示的情况下,当索引的结构发生变化时,就需要在应用程序中修改所使用的每一个提示。在SQL被执行时,优化器本可以很灵活地找出较好的执行计划;但是由于使用提示固定了数据访问路径,从而在很大程度上限制了优化器制定最优执行计划的灵活性。在这里之所以介绍几种错误地使用提示的案例,主要是想通过这些反面教材来提醒读者慎重使用提示。接下来所介绍的提示在不同版本中其具体使用情况会稍微有所不同,在有些版本中某些提示也可能不能被使用。由于在本书中无法顾及所有的版本,敬请读者谅解。3.3.2使用提示实现最优化目标前面已经介绍了在初始化参数中设置优化器模式的原则和相关概念,在为某个查询语句制定执行计划时,可以将优化器模式作为提示来使用,这两者的概念基本相同。建议各位读者应该按照3.1.2节中所介绍的以优化器的选择目标为准则来设置优化器的模式,在有必要时可以使用下面所介绍的提示。下面所介绍的提示并不需要经常使用,当对优化器为某个查询语句所制定的基本执行计划不满意时,最好的办法就是通过使用提示来转换优化器的模式,并观察其转换后的结果,看是否已经达到我们期望的程度。如果只通过转换优化器的模式就可以获得非常好的执行计划,则就没有必要额外使用更为复杂的提示了。ALL_ROWS为实现查询语句整体结果最优化而引导优化器制定最少成本的执行计划。(Minimizetotalresourceconsumption)例:SELECT/*+ALL_ROWS*/„CHOOSE依据SQL中所使用到的表的统计信息存在与否,来决定使用RBO还是CBO。在CHOOSE模式下,如果能够参考表的统计信息,则将按照ALL_ROWS方式执行。例:SELECT/*+CHOOSE*/„FIRST_ROWS为获得最佳响应时间而引导优化器制定最少成本的执行计划。(Minimumresourceusagetoreturnfirstorfirtnrows)例:SELECT/*+FIRST_ROWS*/„SELECT/*+FIRST_ROWS(10)*/„RULE使用基于规则的优化器来实现最优化执行,即引导优化器根据优先顺序规则来决定查询条件中所使用到的索引或运算符的执行顺序来制定执行计划。例:SELECT/*+RULE*/„3.3.3使用提示改变表连接顺序使用下面介绍的提示可以调整表连接的顺序。不仅表连接的方式会影响执行效率,而且表连接的顺序也同样对执行效率有着极大的影响。调整表连接的顺序并不是只能使用下面介绍的提示,在嵌套循环连接方式中也可以让提示来引导优化器使用由驱动查询条件所创建的索引。然而,该方法只有在使用的索引和表连接顺序同时被调整的情况下才比较有效。一般而言,这里所介绍的提示主要在执行多表连接和表之间的连接顺序比较混乱的情况下才使用;也在排序合并连接(SortMergeJoin)方式或哈希连接(HashJoin)方式下,为引导优化器优先执行数据量比较少的表时使用。ORDERED引导优化器按照FROM中所描述的表的顺序执行连接。如果和LEADING提示被一起使用,则LEADING提示将被忽略。例:SELECT/*+ORDERED*/„FROMTAB1,TAB2,TAB3WHERE„由于ORDERED只能够调整表连接的顺序而并不能改变表连接的方式,所以为了改变表的连接方式,经常将USE_NL、USE_MERGE提示与ORDERED提示放在一起使用。例:SELECT/*+ORDEREDUSE_NL(ABC)*/„FROMTAB1a,TAB2b,TAB3cWHERE„LEADING引导优化器使用LEADING指定的表作为表连接顺序中的第一个表。该提示既与FROM中所描述的表的顺序无关,也与作为调整表连接顺序的ORDERD提示不同,并且在使用该提示时并不需要调整FROM中所描述的表的顺序。当该提示与ORDERED提示同时使用时,该提示被忽略。例:SELECT/*+LEADING(bc)*/„FROMCUSTa,ORDER_DETAILb,ITEMcWHEREa.cust_no=b.cust_noANDb.item_no=c.item_noAND„3.3.4表连接方式选择过程中提示的使用在试图使用提示控制表连接方式之前,必须要对各种表连接方式有一个正确而深刻的理解,充分地了解各种连接方式的优缺点。这句话的意思是说没有一种连接方式在任何情况下都始终有效,实现最优化的连接方式会随着具体情况的不同而有所不同。部分范围处理允许与否、索引的结构状态、查询范围、使用频度、内存的可用状态、操作区域的大小(hash_area_size、sort_area_size)等都影响着表连接的执行效率。除此之外,查询语句的执行时间段和系统的类型(OLTP、OLAP等)也影响着表连接的执行效率。表连接方式的具体概念和选择准则将在第6章中予以详细介绍。