最近在學習 MySQL 相關的東西,大致整理一下
MRR —— Multi-Range Read Optimization
MRR 是優化器將隨機 IO 轉化爲順序 IO,以降低 IO 開銷的手段。
二級索引中存儲的是索引列和主鍵值,當查詢列不都存在與索引列中時(即不是覆蓋索引的情況),需要回表操作。然而回表獲取完整用戶記錄可能回產生隨機 IO(當數據量較多且比較分散時,隨機 IO 性能較低,後會單寫一篇「表空間」的筆記),爲減少這種隨機 IO,MySQL 首先只在二級索引中查詢,統計關聯行的主鍵,然後根據主鍵值排序, 最後從聚簇索引中按照主鍵排序獲取完整記錄。
使用 MRR 時的查詢過程:
- 優化器將從二級索引中查詢到的記錄放到緩衝區中;
- 如果緩衝區已滿,或者掃描到二級索引文件末尾,使用快排對緩衝區的數據按照主鍵排序;
- 用戶線程根據緩衝區的內容,從聚簇索引中獲取數據;
- 當緩衝區內容讀取完畢時,重複上述過程,直到掃描結束
如何查看一條 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 也有一些限制條件:
- 不適用於子查詢、不支持主鍵索引
- ICP的優化策略可用於range、ref、eq_ref、ref_or_null 類型的訪問數據方法
EXPLAIN 時,使用 ICP 的查詢會在 Extra 列出現 Using index condition 關鍵字。