MySQL索引优化-索引可见性

MySQL 支持隐式索引,即优化器不使用的索引。适用于主键(显式或隐式)以外的索引。

索引默认是可见的。要显式控制新索引的可见性,可以在 CREATE TABLECREATE INDEXALTER TABLE 的索引定义中使用 VISIBLEINVISIBLE 关键字,举个栗子:

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 ... VISIBLEINVISIBLE 关键字。ALTER INDEX 也是同样,举个栗子:

sql 复制代码
ALTER TABLE t1 ALTER INDEX i_idx INVISIBLE; 
ALTER TABLE t1 ALTER INDEX i_idx VISIBLE;

关于索引可见或不可见的信息,可以从 information_schemaSTATISTICS 表或 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)

参考文档:dev.mysql.com/doc/refman/...

相关推荐
后端码匠4 小时前
MySQL 8.0安装(压缩包方式)
android·mysql·adb
问道飞鱼6 小时前
【数据库知识】Mysql进阶-高可用MHA(Master High Availability)方案
数据库·mysql·adb·高可用·mha
tiging6 小时前
centos7.x下,使用宝塔进行主从复制的原理和实践
数据库·mysql·adb·主从复制
IsPrisoner7 小时前
Go语言安装proto并且使用gRPC服务(2025最新WINDOWS系统)
开发语言·后端·golang
wangcheng86997 小时前
Oracle常用函数-日期时间类型
数据库·sql·oracle
一只fish7 小时前
MySQL 8.0 OCP 1Z0-908 题目解析(2)
数据库·mysql
tan180°8 小时前
Linux进程信号处理(26)
linux·c++·vscode·后端·信号处理
有梦想的攻城狮8 小时前
spring中的@MapperScan注解详解
java·后端·spring·mapperscan
大学生小郑9 小时前
Go语言八股之Mysql基础详解
mysql·面试