Percona 5.6.6里坑人的index_condition_pushdown
1. 确认optimizer_switch的设置:
# 查看版本 | version | 5.6.6-m9-alpha60.1-log | | version_comment | Percona Server with XtraDB (GPL), Release alpha60.1, Revision 285 | mysql> show global variables like 'optimizer_switch'\G *************************** 1. row *************************** Variable_name: optimizer_switch Value: 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 # 看表状态 mysql> show table status like 'mytab'\G *************************** 1. row *************************** Name: mytab Engine: MyISAM Version: 10 Row_format: Dynamic Rows: 44150743 Avg_row_length: 841 Data_length: 47338626468 Max_data_length: 281474976710655 Index_length: 9326730240 Data_free: 10181027160 Auto_increment: 1 Create_time: 2012-12-07 16:17:38 Update_time: 2013-04-27 21:20:39 Check_time: NULL Collation: utf8_general_ci Checksum: NULL Create_options: Comment: # 看建表DDL CREATE TABLE `mytab` ( `pid` int(10) unsigned NOT NULL, `fid` mediumint(8) unsigned NOT NULL DEFAULT '0', `tid` mediumint(8) unsigned NOT NULL DEFAULT '0', `first` tinyint(1) NOT NULL DEFAULT '0', `author` varchar(40) NOT NULL DEFAULT '', `authorid` int(10) unsigned NOT NULL DEFAULT '0', `subject` varchar(80) NOT NULL DEFAULT '', `dateline` int(10) unsigned NOT NULL DEFAULT '0', `message` mediumtext NOT NULL, `useip` varchar(15) NOT NULL DEFAULT '', `invisible` tinyint(1) NOT NULL DEFAULT '0', `anonymous` tinyint(1) NOT NULL DEFAULT '0', `usesig` tinyint(1) NOT NULL DEFAULT '0', `htmlon` tinyint(1) NOT NULL DEFAULT '0', `bbcodeoff` tinyint(1) NOT NULL DEFAULT '0', `smileyoff` tinyint(1) NOT NULL DEFAULT '0', `parseurloff` tinyint(1) NOT NULL DEFAULT '0', `attachment` tinyint(1) NOT NULL DEFAULT '0', `rate` smallint(6) NOT NULL DEFAULT '0', `ratetimes` tinyint(3) unsigned NOT NULL DEFAULT '0', `status` int(10) NOT NULL DEFAULT '0', `tags` varchar(255) NOT NULL DEFAULT '0', `comment` tinyint(1) NOT NULL DEFAULT '0', `replycredit` int(10) NOT NULL DEFAULT '0', `position` int(8) unsigned NOT NULL AUTO_INCREMENT, PRIMARY KEY (`tid`,`position`), UNIQUE KEY `pid` (`pid`), KEY `fid` (`fid`), KEY `authorid` (`authorid`,`invisible`), KEY `dateline` (`dateline`), KEY `invisible` (`invisible`), KEY `displayorder` (`tid`,`invisible`,`dateline`), KEY `first` (`tid`,`first`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; # 看执行计划: Using index condition mysql> explain select * from mytab WHERE tid=3442629 AND position>=558 AND position<572 ORDER BY position DESC; +----+-------------+------------------+-------+----------------------------+---------+---------+------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------------+-------+----------------------------+---------+---------+------+------+-----------------------+ | 1 | SIMPLE | mytab | range | PRIMARY,displayorder,first | PRIMARY | 7 | NULL | 35 | Using index condition | +----+-------------+------------------+-------+----------------------------+---------+---------+------+------+-----------------------+ # 执行倒序查询,非常慢,需要 8.52秒 mysql> select sql_no_cache * from mytab WHERE tid=3442629 AND position>=558 AND position<572 ORDER BY position DESC; … 14 rows in set (8.52 sec) # 如果不是倒序排序,则执行效率很快: mysql> explain select * from mytab WHERE tid=3442629 AND position>=558 AND position<572 ORDER BY position; +----+-------------+------------------+-------+----------------------------+---------+---------+------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------------+-------+----------------------------+---------+---------+------+------+-----------------------+ | 1 | SIMPLE | mytab | range | PRIMARY,displayorder,first | PRIMARY | 7 | NULL | 35 | Using index condition | +----+-------------+------------------+-------+----------------------------+---------+---------+------+------+-----------------------+ # 只需要 0.10秒 mysql> select sql_no_cache * from mytab WHERE tid=3442629 AND position>=558 AND position<572 ORDER BY position; …… 14 rows in set (0.10 sec)
2. 关闭index_condition_pushdown:
mysql> set optimizer_switch="index_condition_pushdown=off"; Query OK, 0 rows affected (0.00 sec)
查看新的执行计划:
mysql> explain select sql_no_cache * from mytab WHERE tid=3442629 AND position >= 558 AND position < 572 ORDER BY position DESC; +----+-------------+------------------+-------+----------------------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------------+-------+----------------------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | mytab | range | PRIMARY,displayorder,first | PRIMARY | 7 | NULL | 35 | Using where | +----+-------------+------------------+-------+----------------------------+---------+---------+------+------+-------------+
查看实际执行时间:
mysql> select sql_no_cache * from mytab WHERE tid=3442629 AND position>=558 AND position<572 ORDER BY position DESC; …… 14 rows in set (0.00 sec) # 只需要 0.00秒
3. 如果把SQL优化成JOIN,即使不关闭 index_condition_pushdown 也可以达到优化后的效果:
mysql> explain SELECT a.* FROM mytab a join (select tid,position from mytab WHERE tid=3442629 AND position>=558 AND position<572 ORDER BY position DESC) b on a.tid=b.tid and a.position=b.position; +----+-------------+------------------+--------+----------------------------+---------+---------+------------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------------+--------+----------------------------+---------+---------+------------------+------+--------------------------+ | 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 27 | NULL | | 1 | PRIMARY | a | eq_ref | PRIMARY,displayorder,first | PRIMARY | 7 | b.tid,b.position | 1 | NULL | | 2 | DERIVED | mytab | range | PRIMARY,displayorder,first | PRIMARY | 7 | NULL | 35 | Using where; Using index | +----+-------------+------------------+--------+----------------------------+---------+---------+------------------+------+--------------------------+ mysql> SELECT SQL_NO_CACHE a.* FROM mytab a join (select tid,position from mytab WHERE tid=3442629 AND position>=558 AND position<572 ORDER BY position DESC) b on a.tid=b.tid and a.position=b.position; …… 14 rows in set (0.00 sec) # 只需要 0.00秒
问题分析:
猜测在MySQL 5.6中,启用index_condition_pushdown之后,会把所有符合过滤条件的数据先全部取出,然后再进行排序。
上面的例子中,由于 tid, position 已是联合主键,因此默认就是正序排序,所以如果不是倒序排序的话,效率还是很高的。
关闭index_condition_pushdown后,优化器认为可以从联合主键索引中取出符合条件的索引记录,并且利用主键完成倒序排序,
最后再根据主键取出相应的行记录,所以效率会更高,这个可以从改造成JOIN后的执行计划得到佐证。
没有源码研究功底,分析的也不一定正确,期待专业人士给个解释吧,呵呵。
评论
aeoluspu (未验证)
周二, 2013/06/18 - 11:39
Permalink
index_push_down mysql5.6里新特性
index_push_down mysql5.6里新特性,有时候用这些不如不用,查询来的快
http://dev.mysql.com/doc/refman/5.6/en/index-condition-pushdown-optimization.html
yejr
周日, 2013/06/23 - 23:09
Permalink
是的,有些时候ICP反而误事,现在还不够完善
是的,有些时候ICP反而误事,现在还不够完善
sunahf (未验证)
周一, 2013/11/18 - 20:23
Permalink
没有测试过,只是从速度上以及sql上看
没有测试过,只是从速度上以及sql上看,速度的差异应该就在排序上。而开启了ICP,正序倒序的速度差异应该再排序的记录数量上。
如果是正序的话,index First Key 为>=判断,所以会search next key,于是找到了=558&& 如果是倒序的话,index First Key 为
以上均为推测,比较懒没有实测过。
不知道博主对于这个问题有没有确定结论了。
yejr
周一, 2014/11/03 - 14:45
Permalink
ICP是5.6新出来的东西,从历史经验来看肯定还不够完善
ICP是5.6新出来的东西,从历史经验来看肯定还不够完善,所以也没花时间去纠结了 :)