這個文章的原始出處找不到了。html
什麼是MRR?mysql
MRR:multi range read。很差解釋,先來看個例子:sql
select * from tb where key_column = x緩存
在沒有MRR的狀況下,它是這樣獲得結果的:ide
1. select key_column, pk_column from tb where key_column=x order by key_column ---> 假設這個結果集是t性能
2. for each row in t ; select non_key_column from tb where pk_column = pk_column_value。(在Oracle裏第2步叫回表?)優化
在有MRR的狀況下,它是這樣執行的:spa
1. select key_column, pk_column from tb where key_column = x order by key_column ---> 假設這個結果集是thtm
2. 將結果集t放在buffer裏面(直到buffer滿了),而後對結果集t按照pk_column排序 ---> 假設排序好的結果集是t_sort排序
3. select non_key_column fromtb where pk_column in (select pk_column from t_sort)
二者的區別主要是兩點:
1. 沒有MRR的狀況下,隨機IO增長,由於從二級索引裏面獲得的索引元組是有序,可是他們在主鍵索引裏面倒是無序的,因此每次去主鍵索引裏面獲得non_key_column的時候都是隨機IO。(若是索引覆蓋,那也就不必利用MRR的特性了,直接從索引裏面獲得全部數據)
2. 沒有MRR的狀況下,訪問主鍵索引的次數也會增長。沒有MRR的狀況下,二級索引裏面獲得多少行,那麼就要去訪問多少次主鍵索引(也不能徹底這樣說,由於MySQL實現了BNL),而有了MRR的時候,次數就大約減小爲以前次數t/buffer_size。
因此說MRR主要解決的就是這兩個問題。
此外,MRR還能夠將某些範圍查詢,拆分爲鍵值對,以此來進行批量的數據查詢。這樣作的好處是能夠在拆分過程當中,直接過濾一些不符合查詢條件的數據。
如:
官方文檔:https://dev.mysql.com/doc/refman/5.7/en/mrr-optimization.html
> SELECT * FROM t WHERE key_part1 >=1000 AND key_part1 < 2000 AND key_part2 = 1000;
表t有(key_part1,key_part2)的聯合索引,所以索引根據key_part1,key_part2的位置關係進行排序。若沒有MRR,此時查詢類型爲Range,SQL優化器會先將key_part1大於1000且小於2000的數據都取出來,即使key_part2不等於1000。取出後再根據key_part2的條件進行過濾。這會致使無用的數據被取出。
若是啓用MRR優化器會使性能有巨大的提高,優化器會先將查詢條件拆分爲(1000,1000),(1001,1000),(1002,1000)....(1999,1000) 最後再根據這些拆分出的條件進行數據的查詢。
是否啓用MRR優化,能夠經過參數optimizer_switch中的flag來控制。當MRR爲on時,表示啓用MRR優化。mrr_cost_based表示是否經過costbased的方式來選擇是否啓用mrr。若設置mrr=on,mrr_cost_based=off,則老是啓用MRR優化。以下:
> SET GLOBAL optimizer_switch='mrr=on,mrr_cost_based=off';
參數read_rnd_buffer_size用來控制鍵值的緩衝區大小。當大於該值時,則執行器對已經緩存的數據根據RowID進行排序,並經過RowID來取得行數據,該值默認是256KB
>show VARIABLES like 'read_rnd_buffer_size';
+----------------------+---------+
| Variable_name | Value |
|----------------------+---------|
| read_rnd_buffer_size | 262144 |
+----------------------+---------+