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)
上。它还定义了一个次要索引 k_d
在列 (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)
。在这种情况下,它可以使用左most 索引前缀 (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 |
+-----------------------+-------+
优化器的 use_index_extensions
标志允许控制优化器是否在确定如何使用 InnoDB
表的次要索引时考虑主键列。默认情况下,use_index_extensions
是启用的。要检查禁用索引扩展是否可以改善性能,请使用以下语句:
SET optimizer_switch = 'use_index_extensions=off';
优化器对索引扩展的使用受到通常限制的限制,例如索引中的键部分数(16)和最大键长度(3072 字节)。