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 位元組) 的一般限制。