How to query inefficient indexes

Updated at:

After multiple indexes are created on a table, some indexes may no longer be used by queries as your business evolves, but they still slow down writes and consume storage. The inefficient index query feature of PolarDB-X can identify inefficient indexes in databases, including local indexes and global secondary indexes (GSIs). Based on the query results, you can make necessary adjustments such as deleting or rebuilding indexes to improve write performance and save storage space without affecting query performance. We recommend that you run inefficient index queries on a regular basis.

Scope of application

  • Local index query: The instance version must be 5.4.20-20260529, 5.4.21-20260509, or later.

  • Global index query: The instance version must be 5.4.17-16859297 or later.

Note

How it works

Inefficient index queries cover two types of indexes, each with different data sources:

  • Local indexes: Each data node (DN) records the access count of each index on physical tables. Compute nodes (CNs) aggregate the data and expose it through the information_schema.logical_index_usage and information_schema.physical_index_usage views.

  • Global secondary indexes (GSIs): After you enable the GSI_STATISTICS_COLLECTION parameter, CNs record the access count and last access time of each GSI. The INSPECT INDEX command provides diagnostic conclusions and optimization suggestions based on this data and index metadata. For more information about GSI diagnostics, see Index diagnostics.

Logical tables and physical tables

PolarDB-X is a distributed database. A table exists at both the logical and physical levels. Understanding the difference helps you correctly interpret the fields in the logical_index_usage and physical_index_usage views.

  • Logical table: The table that you create and access. It is logically a complete table and resides in a logical database. The database names and table names used in daily SQL statements are logical database names and logical table names.

  • Physical table: After a logical table is partitioned, the shard tables are physically stored on DNs and belong to physical databases. A logical table usually corresponds to multiple physical tables. A physical table name typically consists of the logical table name and a partition suffix, such as my_table_xxxx_00000.

CNs maintain the mapping between logical names and physical names. The following table describes the relationships between concepts at each level and the fields in the two views:

Concept

Node

Description

Corresponding field

Logical database

Compute node (CN)

The database you access

LOGICAL_SCHEMA

Logical table

Compute node (CN)

The table you access (logically complete)

LOGICAL_TABLE

Physical database

Data node (DN)

The actual schema that stores data on the DN

OBJECT_SCHEMA

Physical table

Data node (DN)

The actual shard table stored on the DN, including the partition suffix

OBJECT_NAME

Because one logical table corresponds to multiple physical tables, the granularity difference between the two views can be understood as follows:

  • physical_index_usage provides statistics at the physical table level. The same logical index occupies one row per physical partition, showing raw counts.

  • logical_index_usage aggregates the counts of the same logical index across all physical partitions. Each logical index occupies only one row.

Query local index usage

The two views provide statistics at different granularities. Choose the appropriate view based on your troubleshooting goal:

View

Granularity

Primary use case

logical_index_usage

Logical table level (aggregated across all physical partitions)

Recommended for daily use. Determine whether an index should be deleted.

physical_index_usage

Physical table (partition) level

View index statistics on each physical partition for in-depth analysis.

Note

For complete column information of the views, see Metadatabases and data dictionaries.

logical_index_usage view

Query the usage of local indexes through the information_schema.logical_index_usage view to identify unused inefficient indexes. The key fields used in the following examples are described below:

Field

Description

LOGICAL_SCHEMA

The logical database name.

LOGICAL_TABLE

The logical table name. For a GSI table, this is the name of the corresponding primary table.

INDEX_NAME

The index name.

GSI_NAME

The GSI name to which the index belongs. A non-empty value indicates that the row corresponds to a local index on a GSI. An empty value indicates a local index on a regular logical table.

TOTAL_USAGE

The total number of times the index was accessed across all physical partitions.

AVG_USAGE

The average number of times the index was accessed per physical partition.

PARTITION_COUNT

The number of physical partitions that the index covers.

Query examples

  • View the usage of all indexes:

    SELECT * FROM information_schema.logical_index_usage;
  • Find indexes that have never been used (TOTAL_USAGE = 0):

    SELECT * FROM information_schema.logical_index_usage
    WHERE TOTAL_USAGE = 0;
  • Filter by logical table name:

    SELECT * FROM information_schema.logical_index_usage
    WHERE LOGICAL_TABLE = 'my_table';
  • Filter by index name:

    SELECT * FROM information_schema.logical_index_usage
    WHERE INDEX_NAME = 'idx_col_b';
  • Filter by logical database name:

    SELECT * FROM information_schema.logical_index_usage
    WHERE LOGICAL_SCHEMA = 'my_database';
  • Combine filters to query a specific index on a specific table:

    SELECT * FROM information_schema.logical_index_usage
    WHERE LOGICAL_TABLE = 'my_table' AND INDEX_NAME = 'idx_col_b';
  • Find the usage of local indexes on GSIs:

    SELECT * FROM information_schema.logical_index_usage
    WHERE GSI_NAME != '' AND GSI_NAME IS NOT NULL;
  • Sort by usage to find the hottest or coldest indexes:

    SELECT LOGICAL_TABLE, INDEX_NAME, TOTAL_USAGE, AVG_USAGE, PARTITION_COUNT
    FROM information_schema.logical_index_usage
    ORDER BY TOTAL_USAGE DESC;
  • Find inefficient indexes (many partitions but low usage):

    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 view

Before deleting an index, you can use the information_schema.physical_index_usage view to check the usage of the index on each physical partition. The key fields used in the following examples are described below:

Field

Description

OBJECT_SCHEMA

The physical database name (the actual schema name on the DN).

OBJECT_NAME

The physical table name (the actual table name on the DN).

INDEX_NAME

The index name.

COUNT_STAR

The number of times the index was accessed (reads + writes).

LOGICAL_SCHEMA

The logical database name.

LOGICAL_TABLE

The logical table name. For a GSI table, this is the GSI table name.

Query examples

  • View the usage of all indexes on each physical partition:

    SELECT * FROM information_schema.physical_index_usage;
  • Find indexes that have never been used on any physical partition (COUNT_STAR = 0):

    SELECT * FROM information_schema.physical_index_usage
    WHERE COUNT_STAR = 0;
  • Filter by index name:

    SELECT * FROM information_schema.physical_index_usage
    WHERE INDEX_NAME = 'idx_col_b';
  • Filter by logical table name:

    SELECT * FROM information_schema.physical_index_usage
    WHERE LOGICAL_TABLE = 'my_table';
  • Filter by logical database name:

    SELECT * FROM information_schema.physical_index_usage
    WHERE LOGICAL_SCHEMA = 'my_database';
  • View index statistics on GSI physical tables:

    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

Query timeout control

You can set the INDEX_USAGE_QUERY_TIMEOUT parameter to control the timeout for CN to wait for results from all DNs when querying index usage views.

Property

Value

Type

Long

Default value

120 (seconds)

Scope

Session level

  • Method 1: Set through a TDDL hint. This takes effect only for the current query:

    /*+TDDL:cmd_extra(INDEX_USAGE_QUERY_TIMEOUT=30)*/
    SELECT * FROM information_schema.physical_index_usage;
  • Method 2: Set through the SET statement. This takes effect for all subsequent queries in the current session. SET defaults to the SESSION scope, which is equivalent to SET SESSION INDEX_USAGE_QUERY_TIMEOUT=30;:

    SET INDEX_USAGE_QUERY_TIMEOUT=30;
    SELECT * FROM information_schema.physical_index_usage;
  • When a query times out, an error is returned. The error message includes the number of DNs that have completed and those that have not. Example:

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

Clear statistics

To re-observe index usage based on a specific time window, run the following command to reset the statistics to zero, wait for a period of time (such as one month) to collect new statistics, and then query the views.

CLEAR INDEX_USAGE;

Query global index usage

Two methods are available to query inefficient global indexes. Choose the appropriate method based on your goal:

Tool

Output granularity

Primary use case

INSPECT INDEX command

Provides problem diagnosis and optimization suggestions based on statistics and index metadata

Recommended by default. Provides problem identification and two-step optimization suggestions (invisible then drop) in one command.

information_schema.global_indexes view

Raw statistics only

Use when you need to filter by raw fields such as SIZE_IN_MB and CARDINALITY.

Enable global index usage statistics

Diagnosing inefficient global indexes requires index usage data. Before diagnosis, enable the index usage statistics collection feature and allow it to collect data for a period of time (such as 1 day or 1 week, depending on your business traffic cycle).

SET GLOBAL GSI_STATISTICS_COLLECTION=true;
Note

Enabling this feature slightly affects query performance (approximately 1% decrease). After diagnosis is complete, we recommend that you run SET GLOBAL GSI_STATISTICS_COLLECTION=false to disable it.

Diagnose inefficient global indexes

Use the INSPECT INDEX command to diagnose global indexes.

Syntax

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

Parameters

  • MODE: Specifies the diagnosis mode. Three modes are supported.

    • STATIC (default): Diagnoses static issues such as duplicate indexes. These issues can be identified immediately after an index is created.

    • DYNAMIC: Diagnoses dynamic issues such as low index discrimination or indexes that have never been used. These issues can only be discovered after the index has been in use for a period of time. (Requires global index usage statistics to be enabled.)

    • MIXED: Diagnoses both static and dynamic issues. The MIXED mode also requires index usage data to be collected over a period of time for accurate diagnosis results. (Requires global index usage statistics to be enabled.)

  • FROM: Specifies the table.

    • If the FROM clause is omitted, all indexes in the current database are diagnosed by default.

    • If the FROM clause is specified, only the indexes on the specified table are diagnosed.

  • FULL: Specifies the detail level of the output.

    • With the FULL option, detailed diagnosis results are returned, including identified issues and suggestions.

    • Without the FULL option, only suggestions are returned.

Query examples

  • For example, diagnose the following table 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);
  • Run INSPECT FULL INDEX MODE=MIXED\G to get the MIXED mode diagnosis results:

    *************************** 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 describes the issues with the index, such as the same partitioning rule as the primary table, duplicate index columns with other indexes, or USE_COUNT of 0 indicating the index has never been used.

    • The ADVICE field provides two-step optimization suggestions:

      • STEP1: Hide the index by using an invisible index to evaluate the business impact.

      • STEP2: Delete the index after confirming no impact (or rebuild a better index).

      To avoid affecting your business, make sure to execute STEP1 for evaluation before executing the deletion in STEP2.

global_indexes view

To help you tune global indexes, PolarDB-X also provides the INFORMATION_SCHEMA.GLOBAL_INDEXES view for viewing the usage of global indexes. Run the following command to view the global index usage:

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

Focus on the following fields:

Field

Description

SIZE_IN_MB

The storage space occupied by the index.

USE_COUNT

The number of times the index was used after GSI_STATISTICS_COLLECTION was enabled.

LAST_ACCESS_TIME

The last time the index was used after statistics collection was enabled.

CARDINALITY

The cardinality of the index.

ROW_COUNT

The number of rows in the index.

A global index with USE_COUNT consistently at 0 and a large SIZE_IN_MB is a typical inefficient index.