之前转载了 inside君 的 mysql join 原理与调优。发现 Block Nested-Loop 和 mrr 的优化重点落在了回表查询。回顾一下,Block Nested-Loop 主要是在外表和内表的中间加了一个buffer,buffer缓存外表的数据,可以减少内表的io。当查询条件包含辅助索引,查询结果包含查询条件之外的字段时,Block Nested-Loop 同样需要回表查询,而辅助索引不是顺序的,所以回表查询过程会包含大量的随机io,会严重影响查询效率。而针对这个问题,mysql 5.6推出了mrr新特性:
从上图可以发现mrr通过一个额外的内存来对rowid进行排序,然后再顺序地,批量地访问表。这个进行rowid排序的内存大小由参数read_rnd_buffer_size控制,默认256K。
要开启mrr还有一个比较重的参数是在变量optimizer_switch中的mrr和mrr_cost_based选项。mrr选项默认为on,mrr_cost_based选项默认为off。mrr_cost_based选项表示通过基于成本的算法来确定是否需要开启mrr特性。然而,在MySQL当前版本中,基于成本的算法过于保守,导致大部分情况下优化器都不会选择mrr特性。为了确保优化器使用mrr特性,请执行下面的SQL语句:
mysql>set optimizer_switch='mrr=on,mrr_cost_based=off';
请看如下例子 mysql中按序执行如下命令
set @@optimizer_switch = "index_condition_pushdown=off"
explain select * from t_user ,t_user_ref where t_user.id = t_user_ref.userId AND t_user.age > 22
set @@optimizer_switch = "index_condition_pushdown=on"
explain select * from t_user ,t_user_ref where t_user.id = t_user_ref.userId AND t_user.age > 22
set @@optimizer_switch = "index_condition_pushdown=on"
结果如下
首先,结合之前的文章,mysql会自动选取数据少的为驱动表,显然上面查询中 t_user_ref 为1 ,可以得出 t_user_ref 为被驱动表,t_user 为驱动表,也就是外表。
外表驱动表type 为All,说明驱动表没有走索引,而是全表扫(age上有索引)。可能这里存在一个问题,就是age字段有索引,为什么执行计划显示索引实效呢? 这里是由于查询的数量是大表的大部分,应该是30%以上时,索引实效。尝试执行
explain select * from t_user ,t_user_ref wheret_user.id = t_user_ref.userId AND t_user.age > 30
结果如下:
如图,验证 查询的数量是大表的大部分,应该是30%以上时,索引实效。 附 索引实效情况总结:http://blog.sina.com.cn/s/blog_6e322ce7010101i7.html http://www.jianshu.com/p/932bcdf2c89f
根据执行引擎的extra字段 using index condition 是指前面说的回表查询。那么这个extra字段究竟代表什么意思呢?
extra 具体信息 参见 https://my.oschina.net/zimingforever/blog/60233
这里主要区分一下Using index ,using where ,Using index condition 。
首先Using where 性能是最差的,表示优化器需要通过索引回表查询数据,也就是上文说的需要随机io的情况。
using index 是性能较好的,表示直接访问索引就足够获取到所需要的数据,不需要通过索引回表。
那么 using index condition 是什么意思呢?
Using index condition 是在5.6版本后加入的新特性(Index Condition Pushdown);简称ICP。
顾名思义,他是在using where 基础上做了一些优化,参照mysq 官网文档:https://dev.mysql.com/doc/refman/5.6/en/index-condition-pushdown-optimization.html
Index Condition Pushdown (ICP) is an optimization for the case where MySQL retrieves rows from a table using an index. Without ICP, the storage engine traverses the index to locate rows in the base table and returns them to the MySQL server which evaluates the
WHERE
condition for the rows. With ICP enabled, and if parts of the WHERE
condition can be evaluated by using only columns from the index, the MySQL server pushes this part of the WHERE
condition down to the storage engine. The storage engine then evaluates the pushed index condition by using the index entry and only if this is satisfied is the row read from the table. ICP can reduce the number of times the storage engine must access the base table and the number of times the MySQL server must access the storage engine.
那么简单翻译就是说:
首先 mysql server 和 storage engine 是两个组件, server 负责 sql的parse, 执行; storage engine 去真正的 做 数据/index的 读取/写入. 以前是这样: server 命令 storage engine 按 index 把相应的 数据 从 数据表读出, 传给server, server来按 where条件 做选择; 现在 ICP则是在 可能的情况下, 让storage engine 根据index 做判断, 如果不符合 条件 则无须 读 数据表. 这样 节省了disk IO.
因此ICP会在回表查询的时候节省数据库IO 提高性能。如图:
Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行
未完待续...