查询优化器的任务是发现执行SQL查询的最佳方案。大多数查询优化器,包括MySQL的查询优化器,总或多或少地在全部可能的查询评估方案中搜索最佳方案。对于联接查询,MySQL优化器所调查的可能的方案数随查询中所引用的表的数目呈指数增加。对于小数量的表(典型小于7-10),这不是一个问题。然而,当提交的查询更大时,查询优化所花的时间会很容易地成为服务器性能的主要瓶颈。
查询优化的一个更加灵活的方法是容许用户控制优化器详尽地搜索最佳查询评估方案。通常思想是优化器调查的方案越少,它编译一个查询所花费的时间越少。另外一方面,由于优化器跳过了一些方案,它可能错过一个最佳方案。
优化器关于方案数量评估的行为能够经过两个系统变量来控制:html
optimizer_prune_level变量告诉优化器根据对每一个表访问的行数的估计跳过某些方案。咱们的试验显示该类“有根据的猜想”不多错过最佳方案,而且能够大大下降查询编辑次数。这就是为何默认状况该选项为on(optimizer_prune_level=1)。然而,若是你认为优化器错过了一个更好的查询方案,则该选项能够关闭(optimizer_prune_level=0),风险是查询编辑花费的时间更长。请注意即便使用该启发,优化器仍然能够探测呈指数数目的方案。mysql
ptimizer_search_depth变量告诉优化器对于每一个未完成的“将来的”方案,应查看多深,以评估是否应对它进一步扩大。optimizer_search_depth值较小会使查询编辑次数大大减少。例如,若是optimizer_search_depth接近于查询中表的数量,对十二、13或更多表的查询极可能须要几小时甚至几天的时间来编译。同时,若是用optimizer_search_depth等于3或4编辑,对于同一个查询,编译器编译时间能够少于1分钟。若是不能肯定合理的optimizer_search_depth值,该变量能够设置为0,告诉优化器自动肯定该值。
咱们能够经过show variables 来查看这些参数
备注(手册网址:http://doc.mysql.cn/mysql5/refman-5.1-zh.html-chapter)sql
从官方手册上看,能够理解为,MySQL采用了基于开销的优化器,以肯定处理查询的最解方式,也就是说执行查询以前,都会先选择一条自觉得最优的方案,而后执行这个方案来获取结果。在不少状况下,MySQL可以计算最佳的可能查询计划,但在某些状况下,MySQL没有关于数据的足够信息,或者是提供太多的相关数据信息,估测就不那么友好了。
可是感受手册上,并无说MySQL怎么去寻找最优方案呢?
经过查询相应的资料,我的理解以下
MySQL优化器中,一个主要的目标是只要可能就是用索引,并且使用条件最严格的索引来尽量多、尽量快地排除那些不符合索引条件的数据行,说白了就是选择怎样使用索引,固然优化器还受其余的影响。为了更直观,下面将经过例子来讲明。
建立一个表:缓存
CREATE TABLE t8( id1 INT NOT NULL , id2 INT NOT NULL, KEY id1_key(`id1`), KEY id2_key(`id2`) ) ENGINE=MYISAM DEFAULT CHARSET=utf8;
插入几行数据以下:
当我执行以下查询语句时候,查询优化器会怎样进行优化呢?服务器
select * from t8 where id1=1 and id2=0;
固然,MySQL不会傻到,从t8表中的一行开始,而后一行行的去比较,id1与id2。优化器会先分析数据表,得知有索引id1_key与id2_key,若是先判断id1_key的话,而后须要从4行数据中排除3行数据;若是先判断id2_key的话,而后须要从2行中排除1行。对人来讲,这两种方式没有什么区别,可是对于程序而言,先判断id2_key须要较少的计算和磁盘输入输出。所以,查询优化器会规定程序,先去检验id2_key索引,而后在从中挑出id2为0的数据行。
经过下图,咱们能够看出,能够选择的索引有id1_key与id2_key,可是实际用到的索引只有id2_key
若是将SQL语句改成 select * from t8 where id1=1 and id2=0;
执行状况也是同样的,不区分先后。以下图: mysql优化
固然,若是将程序,修改成以下并发
select * from t8 where id1=5 and id2=0;
也能够分析得出,会使用id1_key索引 高并发
固然,若是在建立一个复合索引性能
ALTER TABLE t8 ADD KEY id1_id2_key(`id1`,`id2`)
此时,在此执行select * from t8 where id1=1 and id2=0;
固然会考虑使用id1_id2_key索引。
经过上面的例子,能够理解查询优化器在查询的时候,是选择哪个索引做为最合适的索引。除此,也提示咱们,要慎重选择建立索引。如,上面建立了三个索引(id1_key、id1_key、id1_id2_key),可是优化器优化程序时候,每次只能从中选择一个最合适的,若是建立过多,不只仅是给数据的更新和插入带来了压力,同时也增长了优化器的压力。优化
其实,在上面已经查看过优化器优化过程当中的信息,无非就是使用explain。在这里,在集中说说,里面的参数意义。以下图
id: MySQL Query Optimizer 选定的执行计划中查询的序列号。表示查询中执行 select 子句或操做表的顺序,id值越大优先级越高,越先被执行。id 相同,执行顺序由上至下。
select_type:查询类型,SIMPLE、PRIMARY、UNION、DEPENDENT UNION等。
table:显示这一行的数据是关于哪张表的
type:这是重要的列,显示链接使用了何种类型。从最好到最差的链接类型为const、eq_reg、ref、range、indexhe和all
possible_keys:显示可能应用在这张表中的索引。若是为空,没有可能的索引。能够为相关的域从where语句中选择一个合适的语句
key: 实际使用的索引。若是为null,则没有使用索引。不多的状况下,mysql会选择优化不足的索引。这种状况下,能够在select语句中使用use index(indexname)来强制使用一个索引或者用ignore index(indexname)来强制mysql忽略索引
key_len:使用的索引的长度。在不损失精确性的状况下,长度越短越好
ref:显示索引的哪一列被使用了,若是可能的话,是一个常数
rows:mysql认为必须检查的用来返回请求数据的行数
extra:关于mysql如何解析查询的额外信息。
当咱们在执行select * from t8 where id1=1 and id2=0;
语句的时候,优化器会id1_id2_key索引,但咱们能够经过IGNORE INDEX、 IGNORE INDEX来影响索引的选择
经过FORCE INDEX(索引1[,索引2])或者使用USE INDEX(索引1[,索引2]),来指定使用哪一个索引,也能够指定多个索引,让优化器从中挑选。
可使用IGNORE INDEX(索引1[,索引2])来忽略一些索引,这样优化器,就不会考虑使用这些全部,减小优化器优化时间。
通常状况下,MySQL优化器会自行决定按照哪一种顺序扫描数据表才能最快地检索出数据,可是咱们能够经过STRAGHT_JOIN强制优化器按特定的顺序使用数据表,毕竟优化器作的判断不必定都是最优的。使用原则是,让限制最强的选取操做最早执行。STRAIGHT_JOIN能够放在SELECT后面,也能够放在FROM子句中。
以下图
能够看出,不管from t8,t6仍是from t6,t8,都是先检索t6中的表。可是使用STRAIGHT_JOIN的话,就会按照SQL中顺序。
为何优化器要选择先判断t6中的数据呢?一个主要的缘由,由于t6中数据更少。
若是将t8中数据删除几行后,很明显MySQL优化器选择顺序数据表的顺序就会发生变化。
在高并发的网站中,由于MySQL默认的是写优先,有可能致使一些读操做有效时间内得不到执行机会,HIGH_PRIORITY可使用在select和insert操做中,让MYSQL知道,这个操做优先进行。
LOW_PRIORITY可使用在insert和update操做中,让mysql知道,这个操做将优先权将下降。
INSERT DELAYED告诉MySQL,这个操做将会延时插入。
INSERT DELAYED INTO,是客户端提交数据给MySQL,MySQL返回OK状态给客户端。而这是并非已经将数据插入表,而是存储在内存里面等待排队。当mysql有空余时,再插入。另外一个重要的好处是,来自许多客户端的插入被集中在一块儿,并被编写入一个块。这比执行许多独立的插入要快不少,由于它较少了I/O操做。坏处是,不能返回自动递增的ID,以及系统崩溃时,MySQL尚未来得及插入数据的话,这些数据将会丢失。
在实际开发中,一些数据对实时性要求特别高,或者并不常用(可能几天就执行一次或两次),这样就须要把缓冲关了,无论这条SQL语句是否被执行过,服务器都不会在缓冲区中查找该数据,每次都会从磁盘中读取。由于若是实时性要求特别高,缓存中数据可能和磁盘中的就不一样步,若是数据不常用,被缓存起来,就会占用内存。
在my.ini中的query_cache_type,使用来控制表缓存的。这个变量有三个取值:0,1,2,分别表明了off、on、demand。
0:表示query cache 是关闭。
1:表示查询老是先到查询缓存中查找,即便使用了sql_no_cache仍然查询缓存,由于sql_no_cache只是不缓存查询结果,而不是不使用查询结果。
2:表示只有在使用了SQL_CACHE后,才先从缓冲中查询数据,仍然将查询结果缓存起来。
我本地缓存是关闭的,,以下图。
关于MySQL缓存能够参考这里
(http://blog.csdn.net/hsd2012/article/details/51526707)
参考网址见:http://www.javashuo.com/article/p-rrxnjvnb-bb.html
MySQL索引优化分析,参考网址见:http://www.javashuo.com/article/p-whgwffok-hv.html