所謂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的評測:
<a href="http://www.percona.com/blog/2012/03/21/multi-range-read-mrr-in-mysql-5-6-and-mariadb-5-5/">http://www.percona.com/blog/2012/03/21/multi-range-read-mrr-in-mysql-5-6-and-mariadb-5-5/</a>