所谓MRR,简单的说就是当使用二级索引进行检索并且查询的列需要回表时,先根据检索到的PK值进行排序,然后再回表依次查询聚集索引,从而避免过多的随机IO。
测试示例:
创建一个简单的表:
CREATE TABLE `x1` (
`a` int(11) NOT NULL AUTO_INCREMENT,
`b` int(11) DEFAULT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `b` (`b`)
) ENGINE=InnoDB;
插入大量随机数据:
insert into x1 (b,c) select rand()*100, rand()*10000;
insert into x1 (b,c) select rand()*100, rand()*10000 from x1;
insert into x1 (b,c) select rand()*100, rand()*10000 from x1;
……
…
执行SQL:
root@sb1 04:42:15>set session optimizer_switch=’mrr_cost_based=off';
Query OK, 0 rows affected (0.00 sec)root@sb1 04:42:29>explain select * from x1 where b between 60 and 70 limit 10;
+—-+————-+——-+——-+—————+——+———+——+——–+———————————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+——-+——-+—————+——+———+——+——–+———————————-+
| 1 | SIMPLE | x1 | range | b | b | 5 | NULL | 162690 | Using index condition; Using MRR |
+—-+————-+——-+——-+—————+——+———+——+——–+———————————-+
1 row in set (0.00 sec)
参考代码:MySQL5.6.16
1.优化器阶段:
JOIN::optimize
—> make_join_statistics
—>get_quick_record_count
—>SQL_SELECT::test_quick_select
—>get_key_scans_params
—>check_quick_select
—>DsMrr_impl::dsmrr_info_const
—>handler::multi_range_read_info_const //计算MRR的COST
2.初始化:
JOIN::exec —>do_select —> sub_select
—>join_init_read_record
—>QUICK_RANGE_SELECT::reset
—>ha_innobase::multi_range_read_init
—>DsMrr_impl::dsmrr_init
—>DsMrr_impl::dsmrr_fill_buffer
multi_range_read_next
handler::read_range_first
handler::read_range_next
该步骤会读取请求range的二级索引key范围,并进行快速排序,主函数DsMrr_impl::dsmrr_fill_buffer
3.读取聚集索引记录
JOIN::exec —>do_select—>sub_select—>rr_quick—>
QUICK_RANGE_SELECT::get_next
—>ha_innobase::multi_range_read_next
根据之前排好顺序的Primary Key值,依次读取聚集索引记录
4.Percona的评测:
http://www.percona.com/blog/2012/03/21/multi-range-read-mrr-in-mysql-5-6-and-mariadb-5-5/