1. 程式人生 > >【MySQL技術內幕】30-B+樹索引的使用

【MySQL技術內幕】30-B+樹索引的使用

1、不同應用中B+樹索引的使用

在瞭解了B+樹索引的本質和實現後,下一個需要考慮的問題是怎樣正確地使用B+樹索引,這不是一個簡單的問題。這裡所總結的可能並不適用於所有的應用場合。我所能做的只是概括一個大概的方向。在實際的生產環境使用中,每個DBA和開發人員,還是需要根據自己的具體生產環境來使用索引,並觀察索引使用的情況,判斷是否需要新增索引。不要盲從任何人給你的經驗意見, Think different。 根據第1章的介紹,使用者已經知道資料庫中存在兩種型別的應用,OLTP和OLAP應用。在OLTP應用中,查詢操作只從資料庫中取得一小部分資料,一般可能都在10條記錄以下,甚至在很多時候只取1條記錄,如根據主鍵值來取得使用者資訊,根據訂單號取得訂單的詳細資訊,這都是典型OLTP應用的查詢語句。在這種情況下,B+樹索引建立後,對該索引的使用應該只是通過該索引取得表中少部分的資料。這時建立B+樹索引才是有意義的,否則即使建立了,優化器也可能選擇不使用索引。 對於OLAP應用,情況可能就稍顯複雜了。不過概括來說,在OLAP應用中,都需要訪問表中大量的資料,根據這些資料來產生查詢的結果,這些查詢多是面向分析的查詢,目的是為決策者提供支援。如這個月每個使用者的消費情況,銷售額同比、環比增長的情況。因此在OLAP中索引的新增根據的應該是巨集觀的資訊,而不是微觀,因為最終要得到的結果是提供給決策者的。例如不需要在OLAP中對姓名欄位進行索引,因為很少需要對單個使用者進行查詢。但是對於OLAP中的複雜查詢,要涉及多張表之間的聯接操作,因此索引的新增依然是有意義的。但是,如果聯接操作使用的是 Hash Join,那麼索引可能又變得不是非常重要了,所以這需要DBA或開發人員認真並仔細地研究自己的應用。不過在OLAP應用中,通常會需要對時間欄位進行索引,這是因為大多數統計需要根據時間維度來進行資料的篩選。

2、聯合索引

聯合索引是指對錶上的多個列進行索引。前面討論的情況都是隻對錶上的一個列進行索引。聯合索引的建立方法與單個索引建立的方法一樣,不同之處僅在於有多個索引列。 例如,以下程式碼建立了一張t表,並且索引idx_a_b是聯合索引,聯合的列為(a,b)。

CREATE TABLE t(
a INT,
b INT,
PRIMARY KEY (a),
KEY idx_a_b(a,b)
)ENGINE=INNODB;

那麼何時需要使用聯合索引呢?在討論這個問題之前,先來看一下聯合索引內部的結果。從本質上來說,聯合索引也是一棵B+樹,不同的是聯合索引的鍵值的數量不是1,而是大於等於2。接著來討論兩個整型列組成的聯合索引,假定兩個鍵值的名稱分別為a、b,如圖所示。

從圖可以觀察到多個鍵值的B+樹情況。其實和之前討論的單個鍵值的B+樹並沒有什麼不同,鍵值都是排序的,通過葉子節點可以邏輯上順序地讀出所有資料,就上面的例子來說,即(1,1)、(1,2)、(2,1)、(2,4)、(3,1)、(3,2)。資料按(a,b)的順序進行了存放。

因此,對於查詢 SELECT * FROM TABLE WHERE a= xxx and b=xx,顯然是可以使用(a,b)這個聯合索引的。對於單個的a列查詢 SELECT* FROM TABLE WHERE a=xxx,也可以使用這個(a,b)索引。但對於b列的查詢 SELECT * FROM TABLE WhERE b=xxx,則不可以使用這棵B+樹索引。可以發現葉子節點上的b值為1、2、1、4、1、2,顯然不是排序的,因此對於b列的查詢使用不到(a,b)的索引。 聯合索引的第二個好處是已經對第二個鍵值進行了排序處理。例如,在很多情況下應用程式都需要查詢某個使用者的購物情況,並按照時間進行排序,最後取出最近三次的購買記錄,這時使用聯合索引可以避免多一次的排序操作,因為索引本身在葉子節點已經排序了。來看一個例子,首先根據如下程式碼來建立測試表 buy_log:

CREATE TABLE buy_log(
userid INT UNSIGNED NOT NULL,
buy_date DATE
)ENGINE=InnoDB;
INSERT INTO buy_log VALUES (1,'2009-01-01');
INSERT INTO buy_log VALUES (2,'2009-01-01');
INSERT INTO buy_log VALUES (3,'2009-01-01');
INSERT INTo buy_log VALUES (1,'2009-02-01');
INSERT INTo buy_log VALUEs (3,'2009-02-01');
InSERT INTO buy_log VALUEs (1,'2009-03-01');
INSERT INTo buy_log VALUEs (1,'2009-04-01');
ALTER TABLE buy_log ADD KEY (userid);
ALTER TABLE buy_log ADD KEY (userid,buy_date);

以上程式碼建立了兩個索引來進行比較。兩個索引都包含了 userid欄位。如果只對於userid進行查詢,如: select * from buy_log where userid=2; 則優化器的選擇為:

mysql> explain select * from buy_log where userid=2;
+----+-------------+---------+------------+------+-----------------+--------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys   | key    | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+-----------------+--------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | buy_log | NULL       | ref  | userid,userid_2 | userid | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+-----------------+--------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

可以發現, possible keys在這裡有兩個索引可供使用,分別是單個的userid索引和( userid, buy date)的聯合索引。但是優化器最終的選擇是索引 userid,因為該索引的葉子節點包含單個鍵值,所以理論上一個頁能存放的記錄應該更多。

接著假定要取出 userid為1的最近3次的購買記錄,其SQL語句如下,執行計劃。

mysql> explain select * from buy_log where userid=1 order by buy_date desc limit 3;
+----+-------------+---------+------------+------+-----------------+----------+---------+-------+------+----------+--------------------------+
| id | select_type | table   | partitions | type | possible_keys   | key      | key_len | ref   | rows | filtered | Extra                    |
+----+-------------+---------+------------+------+-----------------+----------+---------+-------+------+----------+--------------------------+
|  1 | SIMPLE      | buy_log | NULL       | ref  | userid,userid_2 | userid_2 | 4       | const |    4 |   100.00 | Using where; Using index |
+----+-------------+---------+------------+------+-----------------+----------+---------+-------+------+----------+--------------------------+
1 row in set, 1 warning (0.01 sec)

同樣的,對於上述的SQL語句既可以使用 userid索引,也可以使用( userid,buy_date)索引。但是這次優化器使用了( userid, buy_date)的聯合索引 userid2,因為在這個聯合索引中 buy date已經排序好了。根據該聯合索引取出資料,無須再對 buy_date做一次額外的排序操作。若強制使用 userid索引,則執行計劃如下所示。

mysql> explain select * from buy_log force index(userid) where userid=1 order by buy_date desc limit 3;
+----+-------------+---------+------------+------+---------------+--------+---------+-------+------+----------+---------------------------------------+
| id | select_type | table   | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra                                 |
+----+-------------+---------+------------+------+---------------+--------+---------+-------+------+----------+---------------------------------------+
|  1 | SIMPLE      | buy_log | NULL       | ref  | userid        | userid | 4       | const |    4 |   100.00 | Using index condition; Using filesort |
+----+-------------+---------+------------+------+---------------+--------+---------+-------+------+----------+---------------------------------------+
1 row in set, 1 warning (0.01 sec)

在Exa選項中可以看到 Using filesort,即需要額外的一次排序操作才能完成查詢而這次顯然需要對列 buy date排序,因為索引 userid中的 buy_date是未排序的正如前面所介紹的那樣,聯合索引(a,b)其實是根據列a、b進行排序,因此下列語句可以直接使用聯合索引得到結果: SELECt .. FROM TABLE WHERE a=xxx ORDER by b 然而對於聯合索引(a,b,c)來說,下列語句同樣可以直接通過聯合索引得到結果: SELECT .. FROM TABLE WHERE a=xxx ORDER BY b SELECT .. FROM TABLE WHERE a=xxx AND b=xxx ORDER BY c 但是對於下面的語句,聯合索引不能直接得到結果,其還需要執行一次 filesort排序操作,因為索引(a,c)並未排序: SELECT .. FROM TABLE WHERE a=xxx ORDER BY C

3、覆蓋索引

InnoDB儲存引擎支援覆蓋索引( covering index,或稱索引覆蓋),即從輔助索引中就可以得到查詢的記錄,而不需要查詢聚集索引中的記錄。使用覆蓋索引的一個好處是輔助索引不包含整行記錄的所有資訊,故其大小要遠小於聚集索引,因此可以減少大量的IO操作。 注意覆蓋索引技術最早是在 InnoDB Plugin中完成並實現。這意味著對於InnoDB版本小於1.0的,或者 MySQL資料庫版本為5.0或以下的, InnoDB儲存引擎不支援覆蓋索引特性。 對於 InnoDe儲存引擎的輔助索引而言,由於其包含了主鍵資訊,因此其葉子節點存放的資料為( primary key1, primary key2,...key1,key2,…)。例如,下列語句都可僅使用一次輔助聯合索引來完成查詢: select key2 FROM table Where key1=xxx: SELECT primary key2, key 2 FROM table Where key1=xxx: SELECT primary key1, key 2 FROM table Where key1=xxx: SELECT primary keyl,primary key2, key2 FROM table Where keyl=xxx: 覆蓋索引的另一個好處是對某些統計問題而言的。還是對於上一小節建立的表buy_log要進行如下查詢:

select count(*) from buy_log;

InnoDB儲存引擎並不會選擇通過查詢聚集索引來進行統計。由於 buy_log表上還有輔助索引,而輔助索引遠小於聚集索引,選擇輔助索引可以減少IO操作,故優化器的選擇如下:

mysql> explain select count(*) from buy_log;
+----+-------------+---------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type  | possible_keys | key    | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | buy_log | NULL       | index | NULL          | userid | 4       | NULL |    7 |   100.00 | Using index |
+----+-------------+---------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

可以看到, possible_keys列為NULL,但是實際執行時優化器卻選擇了userid索引,而列 Extra列的 Using index就是代表了優化器進行了覆蓋索引操作。 此外,在通常情況下,諸如(a,b)的聯合索引,一般是不可以選擇列b中所謂的查詢條件。但是如果是統計操作,並且是覆蓋索引的,則優化器會進行選擇,如下述語句:

mysql> explain SELECT COUNT(*) FROM buy_log Where buy_date>='2011-01-01' and buy_date< '2011-02-01';
+----+-------------+---------+------------+-------+---------------+----------+---------+------+------+----------+--------------------------+
| id | select_type | table   | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+---------+------------+-------+---------------+----------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | buy_log | NULL       | index | NULL          | userid_2 | 8       | NULL |    7 |    14.29 | Using where; Using index |
+----+-------------+---------+------------+-------+---------------+----------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.01 sec)

表 buy_log有( userid, buy_date)的聯合索引,這裡只根據列b進行條件查詢,般情況下是不能進行該聯合索引的,但是這句SQL查詢是統計操作,並且可以利用到覆蓋索引的資訊,因此優化器會選擇該聯合索引。

4、優化器選擇不使用索引的情況

在某些情況下,當執行 EXPLAI命令進行SQL語句的分析時,會發現優化器並沒有選擇索引去查詢資料,而是通過掃描聚集索引,也就是直接進行全表的掃描來得到資料。這種情況多發生於範圍查詢、JOIN連結操作等情況下。例如: SELECT FROM orderdetails Where orderid>10000 and orderid<102000; 上述這句SQL語句查詢訂單號大於10000的訂單詳情,通過命令 SHOW INDEX FROM orderdetails,可觀察到的索引如圖所示。

可以看到表 orderdetails有( OrderID, ProductID)的聯合主鍵,此外還有對於列OrderID的單個索引。上述這句SQL顯然是可以通過掃描 OrderID上的索引進行資料的查詢。然而通過 EXPLAIN命令,使用者會發現優化器並沒有按照 OrderID上的索引來查詢資料,如圖所示。 在 possible keys一列可以看到查詢可以使用 PRIMARY、 OrderID、 OrdersOrder_Details三個索引,但是在最後的索引使用中,優化器選擇了 PRIMARY聚集索引,也就是表掃描( table scan),而非 OrderID輔助索引掃描( index scan)。 這是為什麼呢?原因在於使用者要選取的資料是整行資訊,而 OrderID索引不能覆蓋到我們要査詢的資訊,因此在對 OrderID索引查詢到指定資料後,還需要一次書籤訪問來查詢整行資料的資訊。雖然 OrderID索引中資料是順序存放的,但是再一次進行書籤查詢的資料則是無序的,因此變為了磁碟上的離散讀操作。如果要求訪問的資料量很小,則優化器還是會選擇輔助索引,但是當訪問的資料佔整個表中資料的蠻大一部分時(一般是20%左右),優化器會選擇通過聚集索引來查詢資料。因為之前已經提到過,順序讀要遠遠快於離散讀。因此對於不能進行索引覆蓋的情況,優化器選擇輔助索引的情況是,通過輔助索引查詢的資料是少量的。這是由當前傳統機械硬碟的特性所決定的,即利用順序讀來替換隨機讀的查詢。若使用者使用的磁碟是固態硬碟,隨機讀操作非常快,同時有足夠的自信來確認使用輔助索引可以帶來更好的效能,那麼可以使用關鍵字 FORCE INDEX來強制使用某個索引,如: SELECt FROM orderdetails FORCE INDEX(OrderID) Where orderid>10000 and orderid<102000; 這時的執行計劃如圖所示。

5、索引提示

MySQL資料庫支援索引提示( INDEX HINT),顯式地告訴優化器使用哪個索引。 個人總結以下兩種情況可能需要用到 INDEX HINT:

  • MySQL資料庫的優化器錯誤地選擇了某個索引,導致SQL語句執行的很慢。這種情況在最新的 MySQL資料庫版本中非常非常的少見。優化器在絕大部分情況下工作得都非常有效和正確。這時有經驗的DBA或開發人員可以強制優化器使用某個索引,以此來提高SQL執行的速度。
  • 某SQL語句可以選擇的索引非常多,這時優化器選擇執行計劃時間的開銷可能會大於SL語句本身。例如,優化器分析 Range查詢本身就是比較耗時的操作。這時DBA或開發人員分析最優的索引選擇,通過 Index hint來強制使優化器不進行各個執行路徑的成本分析,直接選擇指定的索引來完成查詢。

在 MySQL資料庫中 Index hint的語法如下

接著來看一個例子,首先根據如下程式碼建立測試表t,並填充相應資料

mysql> create table t( a int, b int, key(a), key(b) )engine=innodb;
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t values(1,1),(1,2),(2,3),(2,4),(1,2);
Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0

然後執行如下的SQL語句: SELECT * FROM t Where a=1 AND b =2; 通過 EXPLAIN命令得到如下所示的執行計劃。

mysql> explain select * from t where a=1 and b=2;
+----+-------------+-------+------------+-------------+---------------+------+---------+------+------+----------+------------------------------------------------+
| id | select_type | table | partitions | type        | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                          |
+----+-------------+-------+------------+-------------+---------------+------+---------+------+------+----------+------------------------------------------------+
|  1 | SIMPLE      | t     | NULL       | index_merge | a,b           | b,a  | 5,5     | NULL |    1 |   100.00 | Using intersect(b,a); Using where; Using index |
+----+-------------+-------+------------+-------------+---------------+------+---------+------+------+----------+------------------------------------------------+
1 row in set, 1 warning (0.01 sec)

結果中的列 possible_keys顯示了上述SQL語句可使用的索引為a,b,而實際使用的索引為列key所示,同樣為a,b。也就是 MySQL資料庫使用a,b兩個索引來完成這一個查詢。列 Extra提示的 Using intersect(b,a)表示根據兩個索引得到的結果進行求交的數學運算,最後得到結果。 如果我們使用 USE INDEX的索引提示來使用a這個索引,如:

select * from t use index(a) where a=1 and b=2;

那麼得到的結果如下所示。

mysql> explain select * from t use index(a) where a=1 and b=2;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t     | NULL       | ALL  | a             | NULL | NULL    | NULL |    5 |    20.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

可以看到,雖然我們指定使用a索引,但是優化器實際選擇的是通過表掃描的方式。 因此, USE INDEX只是告訴優化器可以選擇該索引,實際上優化器還是會再根據自己的判斷進行選擇。而如果使用 FORCE INDEX的索引提示,如:

mysql> explain select * from t force index(a) where a=1 and b=2;
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | t     | NULL       | ref  | a             | a    | 5       | const |    3 |    20.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

可以看到,這時優化器的最終選擇和使用者指定的索引是一致的。因此,如果使用者確定指定某個索引來完成査詢,那麼最可靠的是使用 FORCEⅠNDEX,而不是USE INDEX。

6、Multi-Range Read優化

MySQL5.6版本開始支援 Multi-Range Read(MR)優化。 Multi-Range Read優化的目的就是為了減少磁碟的隨機訪問,並且將隨機訪問轉化為較為順序的資料訪問,這對於IO-bound型別的SQL查詢語句可帶來效能極大的提升。 Multi-Range Read優化可適用於range,ref, eq ref型別的查詢。 MRR優化有以下幾個好處:

  • MRR使資料訪問變得較為順序。在查詢輔助索引時,首先根據得到的查詢結果,按照主鍵進行排序,並按照主鍵排序的順序進行書籤查詢
  • 減少緩衝池中頁被替換的次數
  • 批量處理對鍵值的查詢操作。

對於 InnoDB和 MyISAM儲存引擎的範圍查詢和JOIN查詢操作,MRR的工作方式如下:

  • 將查詢得到的輔助索引鍵值存放於一個快取中,這時快取中的資料是根據輔助索引鍵值排序的。
  • 將快取中的鍵值根據 RowID進行排序。
  • 根據 ROwID的排序順序來訪問實際的資料檔案。

此外,若 InnoDB儲存引擎或者 MyISAM儲存引擎的緩衝池不是足夠大,即不能存放下一張表中的所有資料,此時頻繁的離散讀操作還會導致快取中的頁被替換出緩衝池,然後又不斷地被讀入緩衝池。若是按照主鍵順序進行訪問,則可以將此重複行為降為最低。如下面這句SQL語句 SELECT FROM salaries Where salary>10000 AND salary<40000; salary上有一個輔助索引idx_s,因此除了通過輔助索引查詢鍵值外,還需要通過書籤查詢來進行對整行資料的查詢。當不啟用 Multi-Range Read特性時,看到的執行計劃如圖所示。

 若啟用 Mulit-Range Read特性,則除了會在列 Extra看到 Using index condition外,還會看見 Using MRR選項,如圖所示。

而在實際的執行中會體會到兩個的執行時間差別非常巨大,如下表所示。

是否啟用 Multi-Range Read的執行時間對比
執行時間(秒)
不使用 Multi- Range Read 43.213
使用 Multi- Range Read 4.212

在我的膝上型電腦上,上述兩句語句的執行時間相差10倍之多。可見 Multi-Range Read將訪問資料轉化為順序後查詢效能得到提高。 注意上述測試都是在 MySQL資料庫啟動後直接執行SL查詢語句,此時需確保緩衝池中沒有被預熱,以及需要查詢的資料並不包含在緩衝池中。 此外, Multi-Range Read還可以將某些範圍查詢,拆分為鍵值對,以此來進行批量的資料查詢。這樣做的好處是可以在拆分過程中,直接過濾一些不符合查詢條件的資料,例如 SELECT FROM t Where key_part1 >=1000 and key_part1 <2000 and key_part2 =10000; 表t有(key_part1, key_par2)的聯合索引,因此索引根據 key_part1,key_part2的位置關係進行排序。若沒有 Multi-Read Range,此時查詢型別為 Range,SQL優化器會先將 key_part1大於1000小於2000的資料都取出,即使 key_part2不等於1000待取出行資料後再根據 key_part2的條件進行過濾。這會導致無用資料被取出。如果有大量的資料且其 key_part2不等於100,則啟用 Mulit-Range Read優化會使效能有巨大的提升。 倘若啟用了 Multi- Range Read優化,優化器會先將查詢條件進行拆分,然後再進行資料查詢。就上述查詢語句而言,優化器會將查詢條件拆分為(1000,1000),(1001,1000),(1002,1000),…,(1999,1000),最後再根據這些拆分出的條件進行資料的查詢。 可以來看一個實際的例子,查詢如下: SELECT * FROM salaries Where (from_date between '1986-01-01' AND '1995-01-01') AND (salary between 38000 and 40000); 若啟用了 Multi- Range Read優化,則執行計劃如圖所示。

表 salaries上有對於 salary的索引idxs,在執行上述SQL語句時,因為啟用了Mult-Range Read優化,所以會對查詢條件進行拆分,這樣在列Exta中可以看到 Using MRR選項。 是否啟用 Multi-Range Read優化可以通過引數 optimizer_switch中的標記(flag)來控制。當mrr為on時,表示啟用 Multi-Range Read優化。 mrr_cost_based標記表示是否通過 cost based的方式來選擇是否啟用mrr。若將mrr設為on, mrr_cost_based設為off,則總是啟用 Multi-Range Read優化。例如,下述語句可以將 Multi-Range Read優化總是設為開啟狀態:

mysql> SET optimizer_switch='mrr=on,mrr_cost_based=off';
Query OK, 0 rows affected (0.00 sec)

引數 read_rnd_buffer_size用來控制鍵值的緩衝區大小,當大於該值時,則執行器對已經快取的資料根據 RowID進行排序,並通過RowID來取得行資料。該值預設為256K:

mysql> select @@read_rnd_buffer_size;
+------------------------+
| @@read_rnd_buffer_size |
+------------------------+
|                 262144 |
+------------------------+
1 row in set (0.00 sec)

7、Index Condition pushdown(ICP)優化

和 Multi-Range Read一樣, Index Condition Pushdown同樣是 MySQL5.6開始支援的種根據索引進行查詢的優化方式。之前的 MySQL資料庫版本不支援 Index conditionPushdown,當進行索引查詢時,首先根據索引來查詢記錄,然後再根據 WHERE條件來 過濾記錄。在支援 Index Condition pushdown後, MySQL資料庫會在取出索引的同時,判斷是否可以進行 WHERE條件的過濾,也就是將 WHERE的部分過濾操作放在了儲存引擎層。在某些查詢下,可以大大減少上層SQL層對記錄的索取(etch),從而提高資料庫的整體效能。 Index Condition pushdown優化支援 range、ref、 eq ref、 ref or null型別的查詢,當前支援 MyISAM和 InnoDB儲存引擎。當優化器選擇 Index Condition Pushdown優化時,可在執行計劃的列 Extra看到 Using index condition提示。 注意 NDB Cluster儲存引擎支援 Engine Condition Pushdown優化。不僅可以進行“ Index”的 Condition pushdown,也可以支援非索引的 Condition Pushdown,不過這是由其引擎本身的特性所決定的。另外在 MySQL5.1版本中 NDB Cluster儲存引擎就開始支援 Engine Condition Pushdown優化。 假設某張表有聯合索引( zip code, last name, firset name),並且查詢語句如下:

SELECT * FROM people WHERE zipcode=95054 AND lastname like '%etruria% and address like '%Main street%'; 對於上述語句, MySQL資料庫可以通過索引來定位 zipcode等於95054的記錄,但是索引對 WHERE條件的 lastname LIKE %etruria%' ANd address LIKE %Main Street%'沒有任何幫助。若不支援 Index Condition Pushdown優化,則資料庫需要先通過索引取出所有 zipcode等於95054的記錄,然後再過濾 WHERE之後的兩個條件。 若支援 Index Condition pushdown優化,則在索引取出時,就會進行WHERE條的過濾,然後再去獲取記錄。這將極大地提高查詢的效率。當然, WHERE可以過濾的條件是要該索引可以覆蓋到的範圍。來看下面的SQL語句:

SELECT * FROM salaries Where (from_date between '1986-01-01' AND ' 1995-01-01' AND (salary between 38000 and 40000); 若不啟用 Multi-Range Read優化,則其執行計劃如圖所示。 可以看到列 Extra有 Using index condition的提示。但是為什麼這裡的idx_s索引會使用 Index Condition Pushdown優化呢?因為這張表的主鍵是(emp_no, from_date)的聯合索引,所以idx_s索引中包含了 from_date的資料,故可使用此優化方式。