MySQL 支持隐式索引,即优化器不使用的索引。适用于主键(显式或隐式)以外的索引。
索引默认是可见的。要显式控制新索引的可见性,可以在 CREATE TABLE
、CREATE INDEX
或 ALTER TABLE
的索引定义中使用 VISIBLE
或 INVISIBLE
关键字,举个栗子:
sql
-- 建表
CREATE TABLE t1 (
i INT,
j INT,
k INT,
INDEX i_idx (i) INVISIBLE
) ENGINE = InnoDB;
-- 建索引
CREATE INDEX j_idx ON t1 (j) INVISIBLE;
ALTER TABLE t1 ADD INDEX k_idx (k) INVISIBLE;
如果要修改现有索引的可见性,也同样使用 ALTER TABLE ... VISIBLE
或 INVISIBLE
关键字。ALTER INDEX
也是同样,举个栗子:
sql
ALTER TABLE t1 ALTER INDEX i_idx INVISIBLE;
ALTER TABLE t1 ALTER INDEX i_idx VISIBLE;
关于索引可见或不可见的信息,可以从 information_schema
的 STATISTICS
表或 SHOW INDEX
输出中查询,比如:
sql
select * from information_schema.STATISTICS where TABLE_NAME = 't1' and TABLE_SCHEMA = 'test'
输出:
TABLE_SCHEMA | TABLE_NAME | ... | INDEX_NAME | COLUMN_NAME | IS_VISIBLE |
---|---|---|---|---|---|
i_idx | test | ... | i_idx | i | NO |
k_idx | test | ... | k_idx | k | YES |
执行SQL:
sql
show index from t1
输出:
Table | Key_name | ... | Visible |
---|---|---|---|
t1 | i_idx | ... | NO |
t1 | k_idx | ... | YES |
核心机制和作用
1. 非破坏性测试
通过将索引标记为不可见(ALTER INDEX ... INVISIBLE
),用来 测试删除索引对查询性能的影响 ,数据库优化器将自动忽略该索引,使开发人员能够精准评估索引删除后的查询执行计划变化。此操作仅修改数据字典中的元数据标记,无需重构物理存储结构,能有效规避传统DROP INDEX
操作带来的风险:
- 事务回滚导致的锁表风险
- 统计信息丢失引发的执行计划震荡
- 物理删除索引后重建的I/O资源消耗
2. 快速恢复特性
当确认索引必要性后,执行ALTER INDEX ... VISIBLE
即可在毫秒级恢复索引功能,特别适合大型数据表运维场景。
3. 影响和使用
如果标记索引为不可见之后,有几种方法可以观察到对表查询的影响:
- 对于包含索引提示 的查询,如果索引提示使用了不可见索引,会报错。
- 查询有不同的 EXPLAIN 执行计划。
- 慢查询日志中可能会增多。
optimizer_switch
系统变量的 use_invisible_indexes
标志控制优化器是否使用不可见索引来构建查询执行计划,默认 use_invisible_indexes=off
,优化器将忽略不可见索引,如果设置为on
,则不可见索引仍然不可见,但优化器在构建执行计划时仍会将其考虑在内。
使用 SET_VAR
优化器提示临时更新 optimizer_switch
的值,可以在单次查询期间启用不可见索引,举个栗子:
先随机生成1000条数据到t1表中:
sql
INSERT INTO t1 (i, j, k)
SELECT
RAND() * 10,
RAND() * 10,
RAND() * 10
AS random_text
FROM (
WITH RECURSIVE numbers (n) AS (
SELECT 1 -- 初始值
UNION ALL
SELECT n + 1 FROM numbers -- 递归增加
WHERE n < 1000 -- 停止条件(生成 1000 行)
)
SELECT n FROM numbers
) AS seq;
使用优化器提示执行Explain:
sql
explain select /*+ SET_VAR(optimizer_switch = 'use_invisible_indexes=on') */ * from t1 where i = 1;
输出结果:
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | t1 | ref | i_idx | i_idx | 5 | const | 105 | 100 |
普通查询执行Explain:
sql
explain select * from t1 where i = 1;
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | t1 | ALL | 1000 | 0.1 | Using where |
索引可见性不会影响索引维护。例如,无论索引是可见还是不可见,索引都会根据表行的变化继续更新,唯一索引会防止在列中插入重复内容。
如果一个没有显式定义主键索引的表在 NOT NULL
列上有任何 UNIQUE
索引,那它仍然可能有一个有效的隐式主键。在这种情况下,第一个此类索引对表行施加的约束与显式主键相同,因此不能将该索引设置为不可见。举个栗子:
sql
CREATE TABLE t2 (
i INT NOT NULL,
j INT NOT NULL,
UNIQUE j_idx (j)
) ENGINE = InnoDB;
这个表没有定义主键,但在列 j 上的 NOT NULL 索引对行施加了与主键相同的约束,这种情况没办法设置INVISABLE
:
sql
mysql> ALTER TABLE t2 ALTER INDEX j_idx INVISIBLE;
-- ERROR 3522 (HY000): A primary key index cannot be invisible.
如果在表中添加了一个显式主键:
sql
ALTER TABLE t2 ADD PRIMARY KEY (i);
显式主键不能被隐藏。此外,j 上的唯一索引不再作为隐式主键,所以可以设置INVISIBLE
:
sql
mysql> ALTER TABLE t2 ALTER INDEX j_idx INVISIBLE;
-- Query OK, 0 rows affected (0.03 sec)