MySQL索引擴展(Index Extensions)學習總結

 

MySQL InnoDB的二級索引(Secondary Index)會自動補齊主鍵,將主鍵列追加到二級索引列後面。詳細一點來講,InnoDB的二級索引(Secondary Index)除了存儲索引列key值,還存儲着主鍵的值(而不是指向主鍵的指針)。爲何這樣作呢?由於InnoDB是以彙集索引方式組織數據的存儲,即主鍵值相鄰的數據行緊湊的存儲在一塊兒(索引組織表)。當數據行移動或者發生頁分裂的時候,能夠減小大量的二級索引維護工做。InnoDB移動行時,無需更新二級索引。咱們以官方文檔的例子來測試:html

 

CREATE TABLE t1 (
  i1 INT NOT NULL DEFAULT 0,
  i2 INT NOT NULL DEFAULT 0,
  d DATE DEFAULT NULL,
  PRIMARY KEY (i1, i2),
  INDEX k_d (d)
) ENGINE = InnoDB;

 

 

如上所示,這個t1表包含主鍵和二級索引k_d,二級索引k_d(d)的元組在InnoDB內部實際被擴展成(d,i1,i2),即包含主鍵值。所以在設計主鍵的時候,常見的一條設計原則是要求主鍵字段儘可能簡短,以免二級索引過大(由於二級索引會自動補齊主鍵字段)。mysql

 

 

優化器會考慮擴展二級索引的主鍵列,肯定何時使用以及如何使用該索引。 這樣能夠產生更高效的執行計劃和達到更好的性能。有很多博客介紹索引擴展是從MySQL5.6.9開始引入的。不過我的尚未在官方文檔看到相關資料。sql

 

優化器能夠用擴展的二級索引來進行ref,range,index_merge等類型索引訪問(index access),鬆散的索引掃描(index sacns),鏈接和排序優化,以及min()/max()優化。緩存

 

 

 

 

咱們先來插入測試數據(腳原本自官方文檔):oracle

 

 

INSERT INTO t1 VALUES
(1, 1, '1998-01-01'), (1, 2, '1999-01-01'),
(1, 3, '2000-01-01'), (1, 4, '2001-01-01'),
(1, 5, '2002-01-01'), (2, 1, '1998-01-01'),
(2, 2, '1999-01-01'), (2, 3, '2000-01-01'),
(2, 4, '2001-01-01'), (2, 5, '2002-01-01'),
(3, 1, '1998-01-01'), (3, 2, '1999-01-01'),
(3, 3, '2000-01-01'), (3, 4, '2001-01-01'),
(3, 5, '2002-01-01'), (4, 1, '1998-01-01'),
(4, 2, '1999-01-01'), (4, 3, '2000-01-01'),
(4, 4, '2001-01-01'), (4, 5, '2002-01-01'),
(5, 1, '1998-01-01'), (5, 2, '1999-01-01'),
(5, 3, '2000-01-01'), (5, 4, '2001-01-01'),
(5, 5, '2002-01-01');

 

#默認狀況下,索引擴展(use_index_extensions)選項是開啓的。能夠在當前會話經過修改優化器開關optimizer_switch開啓、關閉此選項。app

 

mysql> show variables like '%optimizer_switch%';
 
mysql> SET optimizer_switch = 'use_index_extensions=off';
Query OK, 0 rows affected (0.00 sec)
 
mysql> EXPLAIN
    -> SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra                    |
+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | t1    | ref  | PRIMARY,k_d   | k_d  | 4       | const |    5 | Using where; Using index |
+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)

 

這種狀況下,優化器不會使用主鍵,由於主鍵由字段(i1,i2)組成,可是該查詢中沒有引用t2字段;優化器會選擇二級索引 k_d(d) 。性能

 

 

咱們將use_index_extensions選項在當前會話開啓,那麼SQL語句的執行計劃會怎樣變化呢?測試

 

mysql> SET optimizer_switch = 'use_index_extensions=on';
Query OK, 0 rows affected (0.00 sec)
 
mysql> EXPLAIN
    -> SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
+----+-------------+-------+------+---------------+------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref         | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+-------------+------+-------------+
|  1 | SIMPLE      | t1    | ref  | PRIMARY,k_d   | k_d  | 8       | const,const |    1 | Using index |
+----+-------------+-------+------+---------------+------+---------+-------------+------+-------------+
1 row in set (0.00 sec)
 
mysql> 

 

clip_image001

 

當use_index_extensions=off的時候,僅使用索引k_d中d列的數據,忽略了擴展的主鍵列的數據。而use_index_extensions=on時,使用了k_d索引中(i1,i2,d)三列的數據。能夠從上面兩種狀況下的explain輸出結果中信息得以驗證。優化

 

key_len:由4變到8,說明不單單使用了d列上的索引,並且使用了擴展的主鍵i1列的數據this

 

ref:由const變爲const,const, 使用了索引的兩部分。

 

rows:從5變爲1,代表InnoDB只須要檢查更少的數據行就能夠產生結果集。

 

Extra:Using index,Using where 變爲Using index。經過索引覆蓋就完成數據查詢,而不須要讀取任何的數據行。官方文檔的介紹以下:

 

The Extra value changes from Using where; Using index to Using index. This means that rows can be read using only the index, without consulting columns in the data row.

 

其實關於這二者的區別,查了不少資料都沒有完全搞清楚Using index,Using whereUsing index的區別。此處不作展開。

 

 

另外,從status信息中Handler_read_%相關狀態值能夠觀察實際執行過程當中索引和數據行的訪問統計。

 

    flush table  關閉已打開的數據表,並清除緩存(表緩存和查詢緩存)。

    flush status 把status計數器清零。

 

 

Handler_read_key:The number of requests to read a row based on a key. If this value is high, it is a good indication that your tables are properly indexed for your queries.

 

Handler_read_next:The number of requests to read the next row in key order. This value is incremented if you are querying an index column with a range constraint or if you are doing an index scan.(此選項代表在進行索引掃描時,按照索引從數據文件裏取數據的次數。)

 

 

 

關閉use_index_extensions狀況下,status的統計信息

 

mysql> SET optimizer_switch = 'use_index_extensions=off';
Query OK, 0 rows affected (0.00 sec)
 
mysql> FLUSH TABLE t1;
Query OK, 0 rows affected (0.00 sec)
 
mysql> FLUSH STATUS;
Query OK, 0 rows affected (0.00 sec)
 
mysql> SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
+----------+
| COUNT(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)
 
mysql> SHOW STATUS LIKE 'handler_read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 0     |
| Handler_read_key      | 1     |
| Handler_read_last     | 0     |
| Handler_read_next     | 5     |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 0     |
+-----------------------+-------+
7 rows in set (0.00 sec)

 

開啓use_index_extensions狀況下,status的統計信息

 

mysql> SET optimizer_switch = 'use_index_extensions=on';
Query OK, 0 rows affected (0.00 sec)
 
mysql> FLUSH TABLE t1;
Query OK, 0 rows affected (0.00 sec)
 
mysql> FLUSH STATUS;
Query OK, 0 rows affected (0.00 sec)
 
mysql> SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
+----------+
| COUNT(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)
 
mysql> SHOW STATUS LIKE 'handler_read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 0     |
| Handler_read_key      | 1     |
| Handler_read_last     | 0     |
| Handler_read_next     | 1     |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 0     |
+-----------------------+-------+
7 rows in set (0.00 sec)
 
mysql> 

   

對比兩個執行計劃,發現Handler_read_next的值從5變爲1,代表索引的訪問效率更高了,減小了數據行的讀取次數。

 

 

本文結合官方文檔Use of Index Extensions和本身理解整理。

 

 

參考資料

 

https://docs.oracle.com/cd/E17952_01/mysql-5.6-en/index-extensions.html

https://dev.mysql.com/doc/refman/5.6/en/index-extensions.html

http://blog.51cto.com/huanghualiang/1557306

http://reckey.iteye.com/blog/2258450

相關文章
相關標籤/搜索