mysql回表簡介


之前寫的《mysql B+Tree索引的一點理解》一文中,介紹了MySQL在使用輔助索引的原理,通過輔助索引進行回表不難理解就相當於Oracle的index skip scan.但是mysql5.6版本中推出了mrr功能,其實就是將隨機訪問的數據,通過內部機制緩存到線程內存read_rnd_buffer_size中,然后進行排序,排序后的數據再訪問主鍵索引,將隨機訪問改變為了順序訪問。近似理解為Oracle中的index range scan

一:優點

1.磁盤和磁頭不再需要來回做機械運動

如果沒有這個功能,那么每獲取一個輔助索引的葉子塊就會遍歷一下主鍵,找到對應的數據--該過程我們又稱為回表。
mrr功能,將這些輔助索引掃描后的數據同一進行緩存,然后一次性訪問主鍵索引,然后找到對應的數據,這樣就大大減少了訪問數據塊的數量

2.可以充分利用磁盤預讀

mysql數據庫有一個預讀功能,也就是訪問一個頁的數據時,將臨近頁也會加載到內存中,剛好需要下一頁的數據時就不再需要進行物理IO

二:案例演示

說明:本測試在mysql 5.7.35中進行測試。

1.表結構

Create Table: CREATE TABLE `salaries` (
  `emp_no` int(11) NOT NULL,
  `salary` int(11) NOT NULL,
  `from_date` date NOT NULL,
  `to_date` date NOT NULL,
  PRIMARY KEY (`emp_no`,`from_date`),
  KEY `idx_salaries_salary` (`salary`),
  CONSTRAINT `salaries_ibfk_1` FOREIGN KEY (`emp_no`) REFERENCES `employees` (`emp_no`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8

2.優化器參數

root@localhost [employees]>show 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,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,prefer_ordering_index=on
1 row in set (0.00 sec)

3.查詢SQL並查看執行計划

從這里並沒有發現該執行步驟使用了mrr功能,還是每行檢索之后訪問主鍵索引,然后進行回表

root@localhost [employees]>explain select * from salaries where salary>10000 and salary<40000;
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+-------+----------+-----------------------+
| id | select_type | table    | partitions | type  | possible_keys       | key                 | key_len | ref  | rows  | filtered | Extra                 |
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+-------+----------+-----------------------+
|  1 | SIMPLE      | salaries | NULL       | range | idx_salaries_salary | idx_salaries_salary | 4       | NULL | 21450 |   100.00 | Using index condition |
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+-------+----------+-----------------------+

4.關閉MySQL成本控制

root@localhost [employees]>set optimizer_switch='mrr_cost_based=off';
Query OK, 0 rows affected (0.00 sec)

root@localhost [employees]>show 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=off,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,prefer_ordering_index=on
1 row in set (0.00 sec)

5.再次執行查看執行計划

這時我們發現,執行計划已經使用了mrr功能,對輔助索引數據進行緩存之后,一次回表,

root@localhost [employees]>explain select * from salaries where salary>10000 and salary<40000;
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+-------+----------+----------------------------------+
| id | select_type | table    | partitions | type  | possible_keys       | key                 | key_len | ref  | rows  | filtered | Extra                            |
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+-------+----------+----------------------------------+
|  1 | SIMPLE      | salaries | NULL       | range | idx_salaries_salary | idx_salaries_salary | 4       | NULL | 21450 |   100.00 | Using index condition; Using MRR |
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+-------+----------+----------------------------------+

但是上面基於成本MySQL為什么沒有使用這種方式呢?

顯然上面回表效率是高效的,但是MySQL優化器對於MRR功能又是相當的悲觀。還是盡可能的選擇索引掃描回表。這是我們需要注意的地方

6.mysql5.7版本之后支持hint

mysql5.7版本之后,我們可以使用hint的方式來強制SQL走mrr

root@localhost [employees]>explain select /*+ mrr(salaries) */ * from salaries where salary>10000 and salary<40000;
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+-------+----------+----------------------------------+
| id | select_type | table    | partitions | type  | possible_keys       | key                 | key_len | ref  | rows  | filtered | Extra                            |
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+-------+----------+----------------------------------+
|  1 | SIMPLE      | salaries | NULL       | range | idx_salaries_salary | idx_salaries_salary | 4       | NULL | 21450 |   100.00 | Using index condition; Using MRR |
+----+-------------+----------+------------+-------+---------------------+---------------------+---------+------+-------+----------+----------------------------------+


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM