mysql查询优化器的提示(hit)
如果对优化器选择的执行计划不满意,可以使用优化器提供的几个提示来控制最终的执行计划,关于每个提示的具体用法,建议直接阅读官方手册,一些提示和版本有直接关系,可以使用的一些提示如下:
high_priority和low_priority:
这个提示告诉mysql,当多个语句同时访问某一个表的时候,哪些语句的优先级相对高一些,哪些语句的优先级相对低一些。
high_priority用于select语句的时候,mysql会将其放到表的队列的最前面,而不是按照常规顺序等待,high_priority还可以用于insert语句,其效果只是简单地抵消了全局low_priority设置对该语句的影响。
low_priority则更好相反,它会让该语句一直处于等待状态,只要队列中还有需要访问同一个表的语句,即使是后到的请求也会插到该语句的前面去。很明显,容易把自己给饿死,low_priority提示在select,insert,update和delete语句中都可以使用。
注意:这两个提示只对使用表锁的存储引擎有效,千万不要在innodb或者其他有细粒度锁机制和并发控制的引擎中使用,即使是在myisam中使用也要注意,因为这两个提示会导致并发插入被禁用,可能会严重降低性能。这两个提示只是简单地控制了mysql访问某个数据表的队列顺序,仅此而已。
delayed:
这个提示对insert和replace有效,mysql会将使用该提示的语句立即返回给客户端,并将插入的行数据当如到缓冲区,然后在表空闲时批量将数据写入,日志系统使用这样的提示非常有效,或者是其他需要写入大量数据但是客户端却不需要等待单挑语句完成的IO应用。但这个用法有一些喜爱你知,并不是所有的存储引擎都支持 ,并且该提示会导致函数last_insert_id()无法正常工作。
straight_join:
这个提示可放置在select关键字之后,也可以放置在任何两个关联表的表名之前,第一个用法是让查询中所有的表按照在语句中出现的顺序进行关联,第二个用法则是固定其前后两个表的关联顺序。当mysql没正确选择关联顺序的时候,或者由于可能的顺序太多导致mysql无法评估所有的关联顺序的时候,straight_join都会很有用,如果关联表可能的顺序太多,可能导致mysql花费大量时间在statistics状态。可以使用explain语句来查看关联顺序,然后加上这个提示再用explain查看有没有变化。
sql_small_result和sql_big_result:
这两个提示只对select有效,他们告诉优化器对group by或者distinct查询如何使用临时表及其排序,sql_small_result告诉优化器结果集会很小,可以将结果集放在内存的索引临时表,以避免排序操作,如果是sql_big_result,则告诉优化器结果集可能非常大,建议使用磁盘临时表做排序操作。
sql_cache和sql_no_cache:
这个提示告诉mysql这个结果集是否应该缓存在查询缓存中,如:select sql_cache|sql_no_cache * from tb_name,紧跟在select关键字后面。
sql_calc_found_rows:
严格来说,这个并不是一个优化器提示,它不会告诉优化器任何关于执行计划的东西,它会让mysql返回的结果集包含更多的相关信息,查询中加上该提示mysql会计算除去limit子句后这个查询要返回的结果集的总数。而实际上只返回limit要求的结果集,结果集总数可以通过found_row()获得这个值。一般不要使用这个提示。
for update和lock in share mode:
这也不是真正的优化器提示,这两个提示主要控制select语句的锁机制,但只对实现了行级锁的引擎有效,使用该提示对符合查询条件的数据行加锁,对于insert .. select语句是不需要这两个提示的,因为对于mysql5.0和更新的版本会默认给这些记录加上读锁。内置的支持这两个提示的引擎就是innodb,另外需要记住的是,这两个提示会让某些优化无法正常使用,如:索引覆盖扫描,innodb不能在不访问主键的情况下排他地锁定行,因为行的版本信息保存在主键中。这两个提示经常被开发滥用,很容易造成服务器的锁争用问题,应该尽可能地避免使用这两个提示。通常可以使用其他更好的方式来实现同样的目的。
use index,ignore index和force index:
这几个提示告诉优化器使用或者不使用或者强制使用哪些所以来查询记录。在mysql5.0和更早的版本中,这些提示并不会影响到优化器选择那个索引进行排序和分组,在5.1和之后
的版本可以通过新增选项for order by和for group by来指定是否对排序和分组有效。force index和use index基本相同,除了一点,force index会告诉优化器全表扫描的成本远远高于索引扫描。哪怕实际上该索引用处不大。优化器也会使用force index指定的索引。
在mysql5.0和更新的版本中,新增了一些参数来控制优化器的行为:
optimizer_search_depth:
这个参数控制优化器在穷举执行计划时的限度,如果查询长时间处于statistics状态,那么可以考虑调低此参数的值,默认为62,即,这个也控制了最大关联表数量。
optimizer_prune_level:
该参数默认是打开的,为1,这让优化器会根据需要扫描的行数来决定是否跳过某些执行计划
optimizer_switch:
这个变量包含了一些开启/关闭优化器特性的标志位,如:mysql5.1中可以通过这个参数来控制禁用索引合并的特性。
5.5默认为:
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on
5.6默认为:
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,subquery_materialization_cost_based=on,use_index_extensions=on
这三个参数,前两个参数是用来控制优化器可以走一些捷径的,这些捷径可以让优化器在处理非常复杂的SQL语句时,仍然可以很高效,但这也可能让优化器错过一些真正最优的执行计划,所以应该根据实际需要来修改这些参数。
要注意:在优化器面前耍小聪明是不好的,因为这样做不但收效甚微,而且给后期维护带来了很多额外的工作量,在mysql版本升级的时候,这个问题就很突出了,你设置的优化器提示很可能会让新版的优化器的优化策略失效。除非特殊需要,否则不要使用这些提示来改变默认的执行计划。mysql5.5和5.6在各方面都有非常大的改进,一般来说升级都很顺利,但仍然建议检查各个细节,可以使用percona toolkit中的pt-upgrade工具来检查在新版本中运行的SQL是否与老版本一样,返回相同的结果。