### 8.3.10 使用索引扩展

[](<>)

[`InnoDB`](innodb-storage-engine.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;
```

此表定义列上的主键`(i1, i2)`.它还定义了二级索引`k_d`在列`(d)`, 但在内部`InnoDB`扩展此索引并将其视为列`(d, i1, i2)`.

优化器在确定如何以及是否使用该索引时会考虑扩展二级索引的主键列。这可以产生更有效的查询执行计划和更好的性能。

优化器可以使用扩展二级索引`参考`,`范围`， 和[`index_merge`](switchable-optimizations.html#optflag_index-merge)索引访问，用于松散索引扫描访问，用于连接和排序优化，以及用于[`最小（）`](aggregate-functions.html#function_min)/[`最大限度（）`](aggregate-functions.html#function_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.html)查询会产生以下结果：

```
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
```

在这两种情况下，`钥匙`表示优化器使用二级索引`k_d`但是[`解释`](explain.html)输出显示了使用扩展索引的这些改进：

-   `key_len`从 4 字节变为 8 字节，表示键查找使用列`d`和`i1`， 不只是`d`.

-   这`参考`值从`常量`到`常量，常量`因为密钥查找使用两个关键部分，而不是一个。

-   这`行`计数从 5 减少到 1，表明`InnoDB`应该需要检查更少的行来产生结果。

-   这`额外的`值从`使用哪里；使用索引`到`使用索引`.这意味着可以仅使用索引读取行，而无需查阅数据行中的列。

    使用扩展索引的优化器行为的差异也可以通过[`显示状态`](show-status.html)：


```
FLUSH TABLE t1;
FLUSH STATUS;
SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01';
SHOW STATUS LIKE 'handler_read%'
```

前面的陈述包括[`同花顺表`](flush.html#flush-tables)和[`冲洗状态`](flush.html#flush-status)刷新表缓存并清除状态计数器。

没有索引扩展，[`显示状态`](show-status.html)产生这个结果：

```
+-----------------------+-------+
| 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.html)产生这个结果。这[`Handler_read_next`](server-status-variables.html#statvar_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     |
+-----------------------+-------+
```

这[`使用索引扩展`](switchable-optimizations.html#optflag_use-index-extensions)国旗[`优化器开关`](server-system-variables.html#sysvar_optimizer_switch)系统变量允许控制优化器在确定如何使用主键列时是否考虑主键列`InnoDB`表的二级索引。默认，[`使用索引扩展`](switchable-optimizations.html#optflag_use-index-extensions)已启用。要检查禁用索引扩展是否可以提高性能，请使用以下语句：

```
SET optimizer_switch = 'use_index_extensions=off';
```

优化器对索引扩展的使用受到索引中键部分的数量（16）和最大键长度（3072 字节）的通常限制。
