InnoDB
會自動將主鍵資料行附加到每個次要索引來擴展它們。請考慮這個資料表定義
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;
此資料表在 (i1, i2)
資料行上定義了主鍵。它也在 (d)
資料行上定義了次要索引 k_d
,但在內部,InnoDB
會擴展此索引並將其視為 (d, i1, i2)
資料行。
最佳化工具在判斷如何以及是否使用該索引時,會考量擴展的次要索引的主鍵資料行。這可以產生更有效率的查詢執行計畫和更好的效能。
最佳化工具可以使用擴展的次要索引來進行 ref
、range
和 index_merge
索引存取、寬鬆索引掃描存取、聯結和排序最佳化,以及 MIN()
/MAX()
最佳化。
以下範例顯示執行計畫如何受到最佳化工具是否使用擴展的次要索引的影響。假設 t1
已填入這些列
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');
現在請考慮這個查詢
EXPLAIN SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01'
執行計畫取決於是否使用擴展的索引。
當最佳化工具不考慮索引擴展時,它會將索引 k_d
視為僅 (d)
。EXPLAIN
查詢會產生此結果
mysql> EXPLAIN SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: ref
possible_keys: PRIMARY,k_d
key: k_d
key_len: 4
ref: const
rows: 5
Extra: Using where; Using index
當最佳化工具將索引擴展納入考量時,它會將 k_d
視為 (d, i1, i2)
。在這種情況下,它可以使用最左邊的索引前綴 (d, i1)
來產生更好的執行計畫
mysql> EXPLAIN SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: ref
possible_keys: PRIMARY,k_d
key: k_d
key_len: 8
ref: const,const
rows: 1
Extra: Using index
在這兩種情況下,key
都表示最佳化工具使用了輔助索引 k_d
,但 EXPLAIN
輸出顯示了使用擴展索引帶來的這些改進:
key_len
從 4 個位元組增加到 8 個位元組,表示索引查找使用了d
和i1
欄位,而不僅僅是d
。ref
值從const
變為const,const
,因為索引查找使用了兩個索引鍵部分,而不是一個。rows
計數從 5 減少到 1,表示InnoDB
應該需要檢查更少的資料列才能產生結果。Extra
值從Using where; Using index
變為Using index
。這表示可以僅使用索引讀取資料列,而無需查詢資料列中的欄位。
使用擴展索引時,最佳化工具行為的差異也可以在 SHOW STATUS
中看到。
FLUSH TABLE t1;
FLUSH STATUS;
SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
SHOW STATUS LIKE 'handler_read%'
前面的語句包括 FLUSH TABLES
和 FLUSH STATUS
,用於清除表快取和清除狀態計數器。
不使用索引擴展時,SHOW STATUS
產生以下結果:
+-----------------------+-------+
| 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 |
+-----------------------+-------+
使用索引擴展時,SHOW STATUS
產生以下結果。 Handler_read_next
值從 5 減少到 1,表示索引使用效率更高。
+-----------------------+-------+
| 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 |
+-----------------------+-------+
系統變數 optimizer_switch
的 use_index_extensions
旗標允許控制最佳化工具在決定如何使用 InnoDB
資料表的輔助索引時,是否將主鍵欄位納入考量。預設情況下,use_index_extensions
已啟用。要檢查停用索引擴展的使用是否可以提高效能,請使用以下語句:
SET optimizer_switch = 'use_index_extensions=off';
最佳化工具對索引擴展的使用受限於索引中索引鍵部分數量 (16) 和最大索引鍵長度 (3072 位元組) 的一般限制。