MySQL 5.6 優化點(MRR、BKA 與 ICP)

最近在學習 MySQL 相關的東西,大致整理一下

MRR —— Multi-Range Read Optimization

MRR 是優化器將隨機 IO 轉化爲順序 IO,以降低 IO 開銷的手段。

二級索引中存儲的是索引列和主鍵值,當查詢列不都存在與索引列中時(即不是覆蓋索引的情況),需要回表操作。然而回表獲取完整用戶記錄可能回產生隨機 IO(當數據量較多且比較分散時,隨機 IO 性能較低,後會單寫一篇「表空間」的筆記),爲減少這種隨機 IO,MySQL 首先只在二級索引中查詢,統計關聯行的主鍵,然後根據主鍵值排序, 最後從聚簇索引中按照主鍵排序獲取完整記錄。

使用 MRR 時的查詢過程:

  1. 優化器將從二級索引中查詢到的記錄放到緩衝區中;
  2. 如果緩衝區已滿,或者掃描到二級索引文件末尾,使用快排對緩衝區的數據按照主鍵排序;
  3. 用戶線程根據緩衝區的內容,從聚簇索引中獲取數據;
  4. 當緩衝區內容讀取完畢時,重複上述過程,直到掃描結束

如何查看一條 SQL 是不是 MRR 呢?我們 EXPLAIN 執行一條 SQL 時,使用 MRR 的查詢會在 Extra 列出現 Using MRR 關鍵字。

optimizer_switch 變量控制是否開啓 MRR,默認是開啓的,關閉的話 set optimizer_switch = 'mrr=off'

ICP —— Index Condition Pushdown Optimization(索引下推)

2020-01-18 更新索引下推的分析,添加 Index Key、Index Filter 與 Table Filter。

這個就比較常見了,MySQL 分層架構中最上面是負責管理連接的,第二層是 Server 層用來語法解析、查詢優化的,最後一層是存儲引擎層來和文件系統打交道的。

強烈推薦閱讀何登成大佬 where 條件在數據庫中提取與應用淺析 這篇文章,where 條件可以分爲三大類,分別是 Index Key(First Key、Last Key)、Index Filter 以及 Table Filter。

Index Key

用於確認索引查詢範圍,既然是範圍肯定有開頭有結束,分別對應 Index First Key 與 Index Last Key。

Index First Key 的提取從索引第一列開始,依次對全部索引列進行如下判斷,判斷 where 條件中是否有針對該列的 >=、= 條件,如果有就將該條件加到 Index First Key 中,並查找下一個索引列;where 條件如果有針對該列的 > 條件,同樣將該條件添加到 Index First Key 中並終止 Index First Key 的提取。

Index Last Key 的提取與上面 First Key 類似,只是變成了 <。仍舊從索引第一列開始,判斷 where 條件是否有針對該列的 <=、= 條件,如果有就將該條件添加到 Index Last Key 中,並查找下一個索引列;where 條件中如果有針對該列的 < 條件,同樣將該條件加到 Index Last Key 中並終止 Index Last Key 的提取。

Index Filter

通過對 Index Key 的提取,我們從二級索引對應的 B+ 樹中確認了二級索引的掃描範圍,但並不代表該範圍內的每一項都滿足查詢條件(這裏說的是二級索引包含的列)。何大佬寫的 Index Filter 查詢過程看了好多遍,實在沒看明白,貼圖過來以後看。

在這裏插入圖片描述
Index First Key 用來確認索引的起始範圍,只進行一次判斷,從 B+ 樹根節點開始往下遍歷,直到找到正確的葉節點位置(並定位其中的索引項)。Index Last Key 則是用來定位索引的終止範圍,對確定了起始範圍之後讀取到的每個索引記錄,均需要判斷是否超過了 Index Last Key 的範圍(超過了就結束索引列的查詢),同時還要與 Index Filter 對比(不滿足 Index Filter 條件就丟棄,繼續讀取下一條記錄)。

Table Filter

所有在索引列中不存在的查詢條件,均屬於 Table Filter。既然這個查詢條件在索引列中不存在,肯定需要回表查詢,何大佬寫的太經典了,直接抄過來了。

Table Filte 是最後一道 where 條件的防線,用於過濾通過前面索引的層層考驗的記錄,此時的記錄已經滿足了 Index First Key 與 Index Last Key 構成的範圍,並且滿足 Index Filter 的條件,回表讀取了完整的記錄,判斷完整記錄是否滿足 Table Filter 中的查詢條件,同樣的,若不滿足,跳過當前記錄,繼續讀取索引的下一條記錄,若滿足,則返回記錄,此記錄滿足了 where 的所有條件,可以返回給前端用戶。

MySQL 5.6 之前不區分 Index Filter 還是 Table Filter,存儲引擎層找到滿足 Index Key 的所有二級索引記錄後,都會回表取完整記錄,然後由 Server 層來對 WHERE 條件做過濾。有 ICP 後,Index Filter 這部分就交給存儲引擎層去做了。

這麼做的優點呢?一是減少了回表的開銷,二是減少了存儲引擎層返回 Server 層的時間。

ICP 支持 InnoDB 和 MyISAM 引擎,對於 InnoDB ICP 只支持二級索引,不支持聚簇索引,因爲聚簇索引的全部數據都從 InnoDB Buffer 中讀取,不涉及磁盤 IO。在二級索引是複合索引且前面的條件過濾性較低的情況下,打開 ICP 可以有效的降低 Server 層和 Engine 層之間交互的次數,從而有效的降低在運行時間。

The goal of ICP is to reduce the number of full-row reads and thereby reduce I/O operations. For InnoDB clustered indexes, the complete record is already read into the InnoDB buffer. Using ICP in this case does not reduce I/O.

ICP 也有一些限制條件:

  1. 不適用於子查詢、不支持主鍵索引
  2. ICP的優化策略可用於range、ref、eq_ref、ref_or_null 類型的訪問數據方法

EXPLAIN 時,使用 ICP 的查詢會在 Extra 列出現 Using index condition 關鍵字。

Reference

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章