如何查询低效索引

更新时间:
复制 MD 格式

一张表建立多个索引后,随着业务演进,部分索引可能不再被查询使用,却仍拖慢写入、占用存储。PolarDB-X 的低效索引查询功能可发现数据库中的低效索引(包括局部索引和全局二级索引),据此对相关索引做出必要调整(删除或重建),从而在不影响业务查询性能的前提下,提升写入性能、节约存储空间。为保持应用性能,建议定期执行低效索引查询。

适用范围

  • 局部索引查询:实例版本需在 5.4.20-20260529 或 5.4.21-20260509 及以上。

  • 全局索引查询:实例版本需在 5.4.17-16859297 及以上。

说明

基本原理

低效索引查询针对两类索引,其统计数据来源不同:

  • 局部索引(LSI):各存储节点(DN)记录每个物理表上索引的访问次数,由计算节点(CN)汇总,通过 information_schema.logical_index_usageinformation_schema.physical_index_usage 两张视图对外提供查询。

  • 全局索引(GSI):开启 GSI_STATISTICS_COLLECTION 开关后,计算节点(CN)会记录每个全局索引的访问次数与最后访问时间。INSPECT INDEX 命令基于这些数据及索引的元信息,给出诊断结论与优化建议。详细的全局索引诊断方法请参见索引诊断

逻辑表与物理表

PolarDB-X 是分布式数据库,一张表存在逻辑与物理两个层面,理解二者的区别有助于正确解读logical_index_usagephysical_index_usage两张视图的字段含义。

  • 逻辑表:您创建和访问的表,逻辑上是完整的一张表,位于逻辑库中。日常 SQL 中书写的库名、表名均为逻辑库名与逻辑表名。

  • 物理表:逻辑表经过分区后,实际存储在各存储节点(DN)上的分片表,归属于物理库。一张逻辑表通常对应多个物理表,物理表名一般由逻辑表名加分区后缀构成(如 my_table_xxxx_00000)。

计算节点(CN)负责维护逻辑名与物理名之间的映射。各层概念与两张视图字段的对应关系如下:

概念

所在节点

含义

对应字段

逻辑库

计算节点(CN)

您访问的数据库

LOGICAL_SCHEMA

逻辑表

计算节点(CN)

您访问的表(逻辑上完整)

LOGICAL_TABLE

物理库

存储节点(DN)

DN 上实际存储数据的Schema

OBJECT_SCHEMA

物理表

存储节点(DN)

DN 上实际存储的分片表(含分区后缀)

OBJECT_NAME

由于一张逻辑表对应多个物理表,两张视图的粒度差异可据此理解:

  • physical_index_usage 按物理表统计,同一逻辑索引在不同物理分区上各占一行,展示原始计数。

  • logical_index_usage 将同一逻辑索引在所有物理分区上的计数聚合,每个逻辑索引仅一行。

查询局部索引使用情况

两张视图提供不同粒度的统计数据,请根据排查目标选择:

视图

统计粒度

主要用途

logical_index_usage

逻辑表级别(所有物理分区汇总)

日常首选。判断“这个索引该不该删”

physical_index_usage

物理表(物理分区)级别

查看索引在各个物理分区上的统计数据,用于深入分析

说明

视图完整的列信息详解请参见元数据库和数据字典

logical_index_usage 视图

通过 information_schema.logical_index_usage 视图查询局部索引的使用情况,直观地识别出哪些是未被使用的低效索引。以下示例涉及的关键字段含义如下:

字段

说明

LOGICAL_SCHEMA

逻辑库名

LOGICAL_TABLE

逻辑表名(若为GSI表,此处为其对应的主表名)

GSI_NAME

全局二级索引名(非GSI时为空)

INDEX_NAME

索引名

TOTAL_USAGE

全分区索引被访问的次数(读 + 写)

PARTITION_COUNT

分区数

AVG_USAGE

平均每分区使用量(TOTAL_USAGE / PARTITION_COUNT)

查询示例

  • 查看所有索引的使用情况:

    SELECT * FROM information_schema.logical_index_usage;
  • 查找从未被使用的索引(TOTAL_USAGE = 0):

    SELECT * FROM information_schema.logical_index_usage
    WHERE TOTAL_USAGE = 0;
  • 按逻辑表名过滤:

    SELECT * FROM information_schema.logical_index_usage
    WHERE LOGICAL_TABLE = 'my_table';
  • 按索引名过滤:

    SELECT * FROM information_schema.logical_index_usage
    WHERE INDEX_NAME = 'idx_col_b';
  • 按逻辑库名过滤:

    SELECT * FROM information_schema.logical_index_usage
    WHERE LOGICAL_SCHEMA = 'my_database';
  • 组合过滤,查询特定表的特定索引:

    SELECT * FROM information_schema.logical_index_usage
    WHERE LOGICAL_TABLE = 'my_table' AND INDEX_NAME = 'idx_col_b';
  • 查找全局索引(GSI)上的局部索引使用情况:

    SELECT * FROM information_schema.logical_index_usage
    WHERE GSI_NAME != '' AND GSI_NAME IS NOT NULL;
  • 按使用量排序,查找最热或最冷的索引:

    SELECT LOGICAL_TABLE, INDEX_NAME, TOTAL_USAGE, AVG_USAGE, PARTITION_COUNT
    FROM information_schema.logical_index_usage
    ORDER BY TOTAL_USAGE DESC;
  • 查找低效索引(分区多但总使用量很低):

    SELECT LOGICAL_TABLE, INDEX_NAME, TOTAL_USAGE, PARTITION_COUNT, AVG_USAGE
    FROM information_schema.logical_index_usage
    WHERE PARTITION_COUNT > 4 AND AVG_USAGE < 10
    ORDER BY AVG_USAGE ASC;

physical_index_usage 视图

删除前如需更保险,可通过 information_schema.physical_index_usage 视图确认索引在每一个物理分区上的使用情况。以下示例涉及的关键字段含义如下:

字段

说明

OBJECT_SCHEMA

物理库名(DN 上的实际 Schema 名)

OBJECT_NAME

物理表名(DN 上的实际表名)

INDEX_NAME

索引名

COUNT_STAR

索引被访问的次数(读 + 写)

LOGICAL_SCHEMA

逻辑库名

LOGICAL_TABLE

逻辑表名(若为 GSI 表,此处为 GSI 表名)

查询示例

  • 查看所有索引在各个物理分区的使用情况:

    SELECT * FROM information_schema.physical_index_usage;
  • 查找各个物理分区中从未被使用的索引(COUNT_STAR = 0):

    SELECT * FROM information_schema.physical_index_usage
    WHERE COUNT_STAR = 0;
  • 按索引名过滤:

    SELECT * FROM information_schema.physical_index_usage
    WHERE INDEX_NAME = 'idx_col_b';
  • 按逻辑表名过滤:

    SELECT * FROM information_schema.physical_index_usage
    WHERE LOGICAL_TABLE = 'my_table';
  • 按逻辑库名过滤:

    SELECT * FROM information_schema.physical_index_usage
    WHERE LOGICAL_SCHEMA = 'my_database';
  • 查看 GSI 物理表上的索引统计:

    SELECT
        OBJECT_SCHEMA,
        OBJECT_NAME,
        INDEX_NAME,
        COUNT_STAR,
        LOGICAL_SCHEMA,
        LOGICAL_TABLE
    FROM information_schema.physical_index_usage
    WHERE LOGICAL_SCHEMA = 'my_database'
      AND LOGICAL_TABLE LIKE 'gsi_name%'
    ORDER BY COUNT_STAR DESC
    LIMIT 100

查询超时控制

通过设置参数 INDEX_USAGE_QUERY_TIMEOUT,控制查询索引使用视图时 CN 等待所有 DN 返回结果的超时时间。

属性

类型

Long

默认值

120(秒)

作用域

Session 级别

  • 方式一:通过 TDDL hint 设置,仅对当前查询生效:

    /*+TDDL:cmd_extra(INDEX_USAGE_QUERY_TIMEOUT=30)*/
    SELECT * FROM information_schema.physical_index_usage;
  • 方式二:通过 SET 设置,对当前会话后续所有查询生效。SET 默认为 Session 作用域,等价于 SET SESSION INDEX_USAGE_QUERY_TIMEOUT=30;

    SET INDEX_USAGE_QUERY_TIMEOUT=30;
    SELECT * FROM information_schema.physical_index_usage;
  • 查询超时时会返回错误,错误信息中包含已完成与未完成的 DN 数量,例如:

    WARNING: Query timed out after 30 seconds. Retrieved results from 2/3 DN nodes.

清除统计信息

需基于明确的时间窗口重新观察索引使用情况时,可先执行以下命令将统计数据清零,等待一段时间(例如一个月)重新收集统计信息后再查询视图。

CLEAR INDEX_USAGE;

查询全局索引使用情况

查询全局索引低效性有两种方式,请按目标选择:

工具

输出粒度

主要用途

INSPECT INDEX 命令

根据统计数据和索引元数据,给出问题诊断与优化建议

默认推荐。一次性给出问题定位与两步优化建议(invisible → drop)

information_schema.global_indexes 视图

仅原始统计数据

需按 SIZE_IN_MBCARDINALITY 等原始字段自定义筛选时使用

开启全局索引使用统计

诊断低效全局索引需要依据索引的使用数据,因此在诊断前,需先打开数据库的“索引使用信息统计”开关,让其收集一段时间(如 1 天、1 周,取决于业务流量周期)的数据。

SET GLOBAL GSI_STATISTICS_COLLECTION=true;
说明

开启该开关会轻微影响查询性能(下降 1% 左右),完成诊断后建议执行 SET GLOBAL GSI_STATISTICS_COLLECTION=false 关闭。

诊断低效全局索引

使用 INSPECT INDEX 命令对全局索引进行诊断

语法

INSPECT [FULL] INDEX [FROM table_name] [MODE={STATIC|DYNAMIC|MIXED}]

参数说明

  • MODE:指定诊断模式,支持 3 种诊断模式。

    • STATIC(默认):用于诊断和发现静态问题,如索引的重复。刚建立索引时即可发现这类问题。

    • DYNAMIC:用于诊断和发现动态问题,如索引列的区分度太低、索引从未被使用。这类问题需要在索引被建立后,业务运行一段时间才能被发现。(需开启全局索引使用统计)

    • MIXED:同时诊断静态和动态问题。MIXED 模式也需要收集一段时间的索引使用数据,才能给出准确诊断结果。(需开启全局索引使用统计)

  • FROM:指定数据表。

    • 不附加 FROM 选项时,默认对当前数据库的所有表的索引进行诊断。

    • 附加 FROM 选项时,仅对指定的数据表进行诊断。

  • FULL:指定输出信息的详细程度。

    • 附加 FULL 选项时,会输出索引的详细诊断结果,包括存在的问题和建议。

    • 不附加 FULL 选项时,仅输出建议。

查询示例

  • 例如,对下表 tb1 进行诊断,其表结构为:

    CREATE TABLE tb1(
      id int,
      name varchar(20),
      code varchar(50),
      primary key(id),
      global index idx_name(`name`) partition by key(name),
      global index idx_name_code(`name`, `code`) partition by key(name, code),
      global index idx_id(`id`) partition by key(id)
    ) partition by key(id);
  • 执行 INSPECT FULL INDEX MODE=MIXED\G,可以得到 MIXED 模式的诊断结果:

    *************************** 1. row ***************************
              SCHEMA: d5
               TABLE: tb1
               INDEX: idx_id
          INDEX_TYPE: GLOBAL INDEX
        INDEX_COLUMN: id
     COVERING_COLUMN:
           USE_COUNT: 0
    LAST_ACCESS_TIME: NULL
      DISCRIMINATION: 0.0
             PROBLEM: ineffective gsi `idx_id` because it has the same rule as primary table
      ADVICE (STEP1): alter table `tb1` alter index `idx_id` invisible;
      ADVICE (STEP2): alter table `tb1` drop index `idx_id`;
    alter table `tb1` add local index `idx_id` (`id`);
    • PROBLEM给出索引存在的问题(如与主表分区规则相同、索引列与其他索引重复、USE_COUNT0从未被使用等)。

    • ADVICE栏给出两步优化建议:

      • STEP1:建议先用invisible index隐藏该索引以评估业务影响。

      • STEP2:在确认无影响后再删除(或删除后重建更优的索引)。

      为避免影响业务,请务必先执行STEP1评估,再执行STEP2的删除。

global_indexes 视图

为方便您对全局索引进行调优,PolarDB-X还提供了INFORMATION_SCHEMA.GLOBAL_INDEXES视图,方便查看PolarDB-X的全局索引的使用情况。执行以下命令,查看全局索引使用情况:

SELECT * FROM information_schema.global_indexes WHERE `TABLE`='tb1'\G

重点关注以下字段:

字段

说明

SIZE_IN_MB

索引占用的空间

USE_COUNT

开启 GSI_STATISTICS_COLLECTION 后,索引被使用的次数

LAST_ACCESS_TIME

开启统计后,索引最后一次被使用的时间

CARDINALITY

索引的基数

ROW_COUNT

索引的行数

USE_COUNT 长期为 0 且 SIZE_IN_MB 较大的全局索引,即为典型的低效索引。