Index Condition Pushdown (ICP) 是 MySQL 5.6 引入的存儲引擎層優化特性:將部分 WHERE 條件「下推」到存儲引擎 (InnoDB/MyISAM) 內部利用索引過濾,**減少回表次數與 Server 層交互**,顯著提升篩選比例高的查詢性能。本文通過原理圖解、Profile 實測、EXPLAIN 特徵識別、七大使用限制,全方位解析 ICP。
WHERE 條件,決定保留/丟棄。**缺點**:大量不滿足條件的行仍需 **回表 + 跨層傳輸**,I/O 與 CPU 雙重浪費。
first_name='Anneke'、last_name LIKE '%sig' 中的 last_name 列在索引中)。**收益**:**回表次數 = 滿足下推條件的行數**,大幅降低 I/O 與層間交互。
employees,聯合索引 idx_emp_fnln (first_name, last_name)SELECT * FROM employees
WHERE first_name='Anneke' AND last_name LIKE '%sig';
first_name 可用索引前綴精確匹配,last_name LIKE '%sig' 無法用索引範圍掃描,**但列在索引中,ICP 可下推過濾**。
| 狀態 | Total Duration | Sending data | EXPLAIN Extra |
|---|---|---|---|
| ICP ON | 0.000603 s | 0.000189 s | Using index condition |
| ICP OFF | 0.000970 s | 0.000735 s (3.9x) | Using where |
**結論**:ICP 開啟後總耗時降為 **62%**,Sending data 階段 (含引擎回表+傳輸) 耗時降為 **26%**。索引條件下推效果顯著。
-- ICP ON
EXPLAIN SELECT * FROM employees WHERE first_name='Anneke' AND last_name LIKE '%sig';
+----+-------------+-----------+------+---------------+--------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+--------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | employees | ref | idx_emp_fnln | idx_emp_fnln | 44 | const | 224 | Using index condition | <-- 關鍵標識
+----+-------------+-----------+------+---------------+--------------+---------+-------+------+-----------------------+
-- ICP OFF
... | Extra | Using where | <-- 無 index condition
| # | 限制條件 | 說明 |
|---|---|---|
| 1 | 訪問類型 | 僅支援 range、ref、eq_ref、ref_or_null,**不支援 index (全索引掃描) / ALL (全表掃描)** |
| 2 | 存儲引擎 | 支援 InnoDB、MyISAM** |
| 3 | 索引類型 | **僅支援二級索引 (Secondary Index)**,**不支援主鍵索引 (Clustered Index)**。主鍵索引葉子節點即完整行,無「回表」概念,下推無意義。 |
| 4 | 下推條件 | 並非所有 WHERE 條件均可下推**。僅當條件列 **包含在所用索引中** 時可下推。不在索引列的條件仍在 Server 層過濾 (EXPLAIN 顯示 Using where 並存)。 |
| 5 | 加速效果 | 取決於 **存儲引擎內通過 ICP 篩選掉的數據比例**。篩選率低 (如唯一值極高) 時收益微乎其微。 |
| 6 | 分區表 | MySQL 5.6 **不支援** 分區表 ICP;**5.7+ 已支援**。 |
| 7 | 覆蓋索引 | 使用覆蓋索引 (Using index) 時不觸發 ICP**。因無需回表,Server 層直接從索引獲取所有列,下推無額外收益。 |
-- 非覆蓋索引 (SELECT *),觸發 ICP
EXPLAIN SELECT * FROM employees WHERE first_name='Anneke' AND last_name='Porenta';
-- Extra: Using index condition
-- 覆蓋索引 (僅查索引列),不觸發 ICP
EXPLAIN SELECT first_name, last_name FROM employees WHERE first_name='Anneke' AND last_name='Porenta';
-- Extra: Using where; Using index (無 index condition)
optimizer_switch='index_condition_pushdown=on' 為預設,**無需手動干預**。WHERE a=1 AND b>10 AND c LIKE 'foo%' → 索引 (a, b, c) 可下推全部三列條件。type=ALL/index,確保查詢有合適索引 (ref/range)。EXPLAIN 檢查 Using index condition,結合 Handler_read_next、Innodb_rows_read 觀察回表減少情況。SET optimizer_switch='index_condition_pushdown=off'。

ICP 是 MySQL 5.6 最實用的優化特性之一,**零代碼改動、零配置成本、自動生效**,對「索引命中但回表篩選率高」的查詢效果立竿見影。理解其原理與邊界,能指導更精準的索引設計與慢查詢分析。