索引下推(INDEX CONDITION PUSHDOWN,簡稱 ICP)是 MySQL 5.6 發佈後針對掃描二級索引的一項優化改進。總的來講是經過把索引過濾條件下推到存儲引擎,來減小 MySQL 存儲引擎訪問基表的次數以及 MySQL 服務層訪問存儲引擎的次數。ICP 適用於 MYISAM 和 INNODB,本篇的內容只基於 INNODB。mysql
1.MySQL 服務層:也就是 SERVER 層,用來解析 SQL 的語法、語義、生成查詢計劃、接管從 MySQL 存儲引擎層上推的數據進行二次過濾等等。sql
2.MySQL 存儲引擎層:按照 MySQL 服務層下發的請求,經過索引或者全表掃描等方式把數據上傳到 MySQL 服務層。函數
3.MySQL 索引掃描:根據指定索引過濾條件(好比 where id = 1) ,遍歷索引找到索引鍵對應的主鍵值後回表過濾剩餘過濾條件。性能
4.MySQL 索引過濾:經過索引掃描而且基於索引進行二次條件過濾後再回表。優化
ICP 就是把以上索引掃描和索引過濾合併在一塊兒處理,過濾後的記錄數據下推到存儲引擎後的一種索引優化策略。這樣作的優勢以下:spa
1.減小了回表的操做次數。code
2.減小了上傳到 MySQL SERVER 層的數據。索引
ICP 默認開啓,可經過優化器開關參數關閉 ICP:optimizer_switch='index_condition_pushdown=off' 或者是在 SQL 層面經過 HINT 來關閉。rem
接下來,詳細看下不適用 ICP、使用 ICP 的詳細示例來理清 ICP 的概念。it
MySQL 存儲引擎層只把知足索引鍵值對應的整行表記錄一條一條取出,而且上傳給 MySQL 服務層。
MySQL 服務層對接收到的數據,使用 SQL 語句後面的 where 條件過濾,直處處理完最後一行記錄,再一塊兒返回給客戶端。
假設 SQL 語句爲:
(localhost:mysqld.sock)|(ytt)>select * from t1 where r1 = 1 and r2 like '%dog%' and r4 = 5\G *************************** 1. row *************************** id: 28965 f1: 81 f2: 89 f3: 100 f4: 35 r1: 1 r2: 12844bda dog 11ea a051 08002753f58d r3: 17 r4: 5 1 row in set (0.00 sec)
關閉 ICP 的處理流程大概如圖 1:
MySQL 存儲引擎層,先根據過濾條件中包含的索引鍵肯定索引記區間,再在這個區間的記錄上使用包含索引鍵的其餘過濾條件進行過濾,以後規避掉不知足的索引記錄,只根據知足條件的索引記錄回表取回數據上傳到 MySQL 服務層。
MySQL 服務層對接收到的數據,使用 where 子句中不包含索引列的過濾條件作最後的過濾,而後返回數據給客戶端。
以下圖所示:
上面兩張圖很明顯的對比出開啓 ICP 比不開啓 ICP 的效率。返回數據這一塊虛線表示規避掉的記錄,開啓 ICP 很明顯減小了上傳到 MySQL 存儲引擎層、MySQL 服務層的記錄條數,節省了 IO。
查看語句是否用了 ICP,只須要對語句進行 EXPLAIN,在 EXTRA 信息裏能夠看到 ICP 相關信息。
如下爲分別爲關閉 ICP 與開啓 ICP 的 EXPLAIN 結果:
(localhost:mysqld.sock)|(ytt)>explain select /*+ no_icp (t1) */ * from t1 where r1 = 1 and r2 like '%dog%' and r4 = 5\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: ref possible_keys: idx_r4,idx_u1 key: idx_u1 key_len: 5 ref: const rows: 325 filtered: 0.12 Extra: Using where 1 row in set, 1 warning (0.00 sec) (localhost:mysqld.sock)|(ytt)>explain select * from t1 where r1 = 1 and r2 like '%dog%' and r4 = 5\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: ref possible_keys: idx_r4,idx_u1 key: idx_u1 key_len: 5 ref: const rows: 325 filtered: 0.12 Extra: Using index condition; Using where 1 row in set, 1 warning (0.00 sec)
其中 extra 裏顯示 「Using index condition」 就表明用了 ICP。不過這個信息有點過於簡單了,除了 EXTRA 列結果顯示不一樣外,其餘的列結果都同樣,無法從執行計劃結果判斷 ICP 的優略。
能夠經過如下幾種方法來查看 ICP 帶來的直觀性能提高。
1.show status like '%handler%'
show status 語句能夠查看對存儲引擎的相關指標監控結果。從如下結果能夠看出:指標 Handler_read_next(表示 MySQL 存儲引擎按照索引鍵順序讀取下一行記錄的請求數,也就是說這個值表示按照索引鍵值來訪問基表的請求數)在沒有開啓 ICP 時,值爲 325,也就是說對基表讀取請求 325 次;而開啓 ICP 後,這個值僅有 14 次。因此開啓 ICP 效率提高很明顯。
(localhost:mysqld.sock)|(ytt)>flush status; Query OK, 0 rows affected (0.01 sec) (localhost:mysqld.sock)|(ytt)> select /*+ no_icp (t1) */ * from t1 where r1 = 1 and r2 like '%dog%' and r4 = 5\G *************************** 1. row *************************** id: 28965 f1: 81 f2: 89 f3: 100 f4: 35 r1: 1 r2: 12844bda dog 11ea a051 08002753f58d r3: 17 r4: 5 1 row in set (0.00 sec) (localhost:mysqld.sock)|(ytt)>show status like '%handler%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Handler_commit | 1 | | Handler_delete | 0 | | Handler_discover | 0 | | Handler_external_lock | 2 | | Handler_mrr_init | 0 | | Handler_prepare | 0 | | Handler_read_first | 0 | | Handler_read_key | 1 | | Handler_read_last | 0 | | Handler_read_next | 325 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 0 | | Handler_rollback | 0 | | Handler_savepoint | 0 | | Handler_savepoint_rollback | 0 | | Handler_update | 0 | | Handler_write | 0 | +----------------------------+-------+ 18 rows in set (0.00 sec) (localhost:mysqld.sock)|(ytt)>flush status; Query OK, 0 rows affected (0.01 sec) (localhost:mysqld.sock)|(ytt)>select * from t1 where r1 = 1 and r2 like '%dog%' and r4 = 5\G *************************** 1. row *************************** id: 28965 f1: 81 f2: 89 f3: 100 f4: 35 r1: 1 r2: 12844bda dog 11ea a051 08002753f58d r3: 17 r4: 5 1 row in set (0.00 sec) (localhost:mysqld.sock)|(ytt)>show status like '%handler%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Handler_commit | 1 | | Handler_delete | 0 | | Handler_discover | 0 | | Handler_external_lock | 2 | | Handler_mrr_init | 0 | | Handler_prepare | 0 | | Handler_read_first | 0 | | Handler_read_key | 1 | | Handler_read_last | 0 | | Handler_read_next | 14 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 0 | | Handler_rollback | 0 | | Handler_savepoint | 0 | | Handler_savepoint_rollback | 0 | | Handler_update | 0 | | Handler_write | 0 | +----------------------------+-------+ 18 rows in set (0.00 sec) (localhost:mysqld.sock)|(ytt)>
2.開啓 profiles
查看 profile 結果的整體時間,關閉 ICP 爲:0.00101900,開啓ICP爲:0.00100325。時間上 ICP 佔優點。
(localhost:mysqld.sock)|(ytt)>set profiling=1; Query OK, 0 rows affected, 1 warning (0.00 sec) (localhost:mysqld.sock)|(ytt)>show profiles; Empty set, 1 warning (0.00 sec) (localhost:mysqld.sock)|(ytt)> select /*+ no_icp (t1) */ * from t1 where r1 = 1 and r2 like '%dog%' and r4 = 5\G *************************** 1. row *************************** id: 28965 f1: 81 f2: 89 f3: 100 f4: 35 r1: 1 r2: 12844bda dog 11ea a051 08002753f58d r3: 17 r4: 5 1 row in set (0.00 sec) (localhost:mysqld.sock)|(ytt)> select * from t1 where r1 = 1 and r2 like '%dog%' and r4 = 5\G *************************** 1. row *************************** id: 28965 f1: 81 f2: 89 f3: 100 f4: 35 r1: 1 r2: 12844bda dog 11ea a051 08002753f58d r3: 17 r4: 5 1 row in set (0.00 sec) (localhost:mysqld.sock)|(ytt)>show profiles\G *************************** 1. row *************************** Query_ID: 1 Duration: 0.00101900 Query: select /*+ no_icp (t1) */ * from t1 where r1 = 1 and r2 like '%dog%' and r4 = 5 *************************** 2. row *************************** Query_ID: 2 Duration: 0.00100325 Query: select * from t1 where r1 = 1 and r2 like '%dog%' and r4 = 5 2 rows in set, 1 warning (0.00 sec)
任何須要下推到底層存儲層的操做通常都有諸多限制,MySQL ICP 也不例外,ICP 限制以下:
1.ICP 僅用於須要訪問基表全部記錄時使用,適用的訪問方法爲:range、ref、eq_ref、ref_or_null。我上面舉的例子便是 ref 類型,ICP 尤爲是對聯合索引的部分列模糊查找很是有效。
2.ICP 一樣適用於分區表。
3.ICP 的目標是減小全行記錄讀取,從而減小 I/O 操做,僅用於二級索引。主鍵索引自己便是表數據,不存在下推操做。
4.ICP 不支持基於虛擬列上創建的索引,好比函數索引。
5.ICP 不支持引用子查詢的條件。
關於 MySQL 的技術內容,大家還有什麼想知道的嗎?趕忙留言告訴小編吧!