Skip to content

Oracle 索引维护

索引维护概述

索引维护是 Oracle 数据库性能管理的重要组成部分,定期的索引维护能够确保索引的高效运行,提高查询性能,减少系统资源消耗。索引维护包括索引重建、索引统计收集、索引监控和索引优化等任务。

索引维护的目标是:

  • 保持索引的高效性
  • 减少索引碎片
  • 确保索引统计信息的准确性
  • 识别和删除无用的索引
  • 优化索引结构

索引维护的必要性

索引碎片

频繁的 DML 操作(插入、更新、删除)会导致索引产生碎片,影响索引的性能。索引碎片主要包括:

  • 内部碎片:索引块内部的空闲空间
  • 外部碎片:索引块之间的空闲空间
  • 逻辑碎片:索引键值的顺序与物理存储顺序不一致

统计信息过时

不准确或过时的统计信息会导致优化器生成错误的执行计划,影响查询性能。统计信息包括:

  • 表的行数和块数
  • 列的选择性和分布
  • 索引的高度和叶子块数量

无用的索引

随着业务的发展和查询模式的变化,一些索引可能不再被使用,成为无用的索引。无用的索引会导致:

  • 存储资源浪费
  • DML 操作变慢
  • 索引维护成本增加

索引维护任务

1. 索引重建

索引重建是最常用的索引维护任务,能够消除索引碎片,优化索引结构。

什么时候需要重建索引

  • 索引碎片率超过 30%
  • 索引高度超过 4 层
  • 索引的叶子块使用率低于 50%
  • 查询性能明显下降
  • 索引统计信息不准确

索引重建方法

方法 1:使用 ALTER INDEX REBUILD
sql
-- 重建索引
ALTER INDEX SCOTT.IDX_EMP_DEPT REBUILD;

-- 在线重建索引(允许并发 DML)
ALTER INDEX SCOTT.IDX_EMP_DEPT REBUILD ONLINE;

-- 重建分区索引
ALTER INDEX SCOTT.IDX_SALES_DATE REBUILD PARTITION SALES_Q1_2023;

-- 在线重建分区索引
ALTER INDEX SCOTT.IDX_SALES_DATE REBUILD PARTITION SALES_Q1_2023 ONLINE;
方法 2:使用 ALTER INDEX COALESCE

COALESCE 命令能够合并索引的叶子块,减少索引碎片,但不重建索引结构。

sql
-- 合并索引
ALTER INDEX SCOTT.IDX_EMP_DEPT COALESCE;

-- 合并分区索引
ALTER INDEX SCOTT.IDX_SALES_DATE COALESCE PARTITION SALES_Q1_2023;
方法 3:使用 CREATE INDEX ... WITH ONLINE DDL

对于大型索引,可以使用 CREATE INDEX ... WITH ONLINE DDL 命令重建索引。

sql
-- 使用 ONLINE DDL 重建索引
CREATE INDEX SCOTT.IDX_EMP_DEPT_NEW ON SCOTT.EMP(department_id) WITH ONLINE DDL;
DROP INDEX SCOTT.IDX_EMP_DEPT;
ALTER INDEX SCOTT.IDX_EMP_DEPT_NEW RENAME TO IDX_EMP_DEPT;

2. 索引统计信息收集

准确的统计信息对于优化器生成正确的执行计划至关重要。Oracle 提供了 DBMS_STATS 包用于收集统计信息。

收集统计信息的方法

方法 1:收集表和索引的统计信息
sql
-- 收集表和索引的统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS(
    ownname => 'SCOTT',
    tabname => 'EMP',
    cascade => TRUE,
    estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
    method_opt => 'FOR ALL COLUMNS SIZE AUTO',
    degree => DBMS_STATS.AUTO_DEGREE
);
方法 2:仅收集索引的统计信息
sql
-- 仅收集索引的统计信息
EXEC DBMS_STATS.GATHER_INDEX_STATS(
    ownname => 'SCOTT',
    indname => 'IDX_EMP_DEPT',
    estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
    degree => DBMS_STATS.AUTO_DEGREE
);
方法 3:收集分区索引的统计信息
sql
-- 收集分区索引的统计信息
EXEC DBMS_STATS.GATHER_INDEX_STATS(
    ownname => 'SCOTT',
    indname => 'IDX_SALES_DATE',
    partname => 'SALES_Q1_2023',
    estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
    degree => DBMS_STATS.AUTO_DEGREE
);

统计信息收集策略

  • 手动收集:适用于小型表或需要立即更新统计信息的场景
  • 自动收集:使用 Oracle 自动维护任务,适用于大多数场景
  • 增量收集:适用于分区表,只收集新增或修改的分区的统计信息

3. 索引监控

定期监控索引的使用情况,能够识别无用的索引,优化索引结构。

监控索引使用情况

方法 1:使用索引监控功能
sql
-- 启用索引监控
ALTER INDEX SCOTT.IDX_EMP_DEPT MONITORING USAGE;

-- 查看索引监控结果
SELECT * FROM V$OBJECT_USAGE WHERE index_name = 'IDX_EMP_DEPT';

-- 禁用索引监控
ALTER INDEX SCOTT.IDX_EMP_DEPT NOMONITORING USAGE;
方法 2:使用 AWR 报告

AWR 报告中的 "SQL Plan Statistics" 部分提供了索引使用的统计信息,包括:

  • 索引的使用次数
  • 索引的逻辑读和物理读
  • 索引的 CPU 时间和执行时间
方法 3:使用 ASH 报告

ASH 报告能够显示索引的实时使用情况,包括:

  • 当前正在使用的索引
  • 索引的等待事件
  • 索引的使用频率

4. 索引优化

索引优化包括调整索引结构、删除无用的索引、优化索引设计等任务。

删除无用的索引

sql
-- 查看最近 30 天未使用的索引
SELECT
    owner,
    index_name,
    table_name
FROM
    dba_indexes
WHERE
    owner NOT IN ('SYS', 'SYSTEM', 'SYSMAN', 'DBSNMP')
    AND index_name NOT IN (
        SELECT DISTINCT index_name
        FROM dba_hist_sql_plan
        WHERE
            operation LIKE '%INDEX%'
            AND snap_id IN (SELECT snap_id FROM dba_hist_snapshot WHERE begin_interval_time > SYSDATE - 30)
    );

-- 删除无用的索引
DROP INDEX SCOTT.IDX_EMP_DEPT_UNUSED;

调整索引结构

  • 调整复合索引的列顺序
  • 增加或删除索引列
  • 改变索引类型
  • 调整索引的存储参数
sql
-- 调整索引的存储参数
ALTER INDEX SCOTT.IDX_EMP_DEPT STORAGE (INITIAL 10M NEXT 5M PCTINCREASE 0);

优化索引设计

  • 根据查询模式调整索引设计
  • 合并冗余索引
  • 优化索引的列选择性
  • 考虑使用索引压缩

索引维护计划

日常维护任务

  • 监控索引使用情况:每天查看索引监控结果,识别无用的索引
  • 监控索引碎片:每天检查索引碎片率,识别需要重建的索引
  • 监控索引统计信息:每天检查索引统计信息的准确性

每周维护任务

  • 收集统计信息:每周收集一次表和索引的统计信息
  • 重建碎片化的索引:每周重建碎片率超过 30% 的索引
  • 分析索引使用情况:每周分析索引使用情况,识别需要优化的索引

每月维护任务

  • 全面索引审计:每月进行一次全面的索引审计,包括索引设计、索引使用情况和索引性能
  • 优化索引结构:每月根据审计结果优化索引结构
  • 删除无用的索引:每月删除确认无用的索引

季度维护任务

  • 索引设计评估:每季度评估索引设计,根据业务需求调整索引设计
  • 索引性能测试:每季度进行索引性能测试,验证索引维护的效果
  • 索引维护策略调整:每季度根据索引性能测试结果调整索引维护策略

索引维护最佳实践

1. 选择合适的维护窗口

  • 选择业务低峰期进行索引维护
  • 对于大型索引,考虑使用在线重建
  • 提前通知相关业务团队
  • 监控维护操作的进度和资源使用

2. 优化维护操作

  • 使用并行度提高维护操作的速度
  • 对于大型表,考虑使用增量统计信息收集
  • 使用 ONLINE 选项允许并发 DML 操作
  • 避免在维护期间进行大量的 DML 操作

3. 监控维护操作

  • 监控维护操作的进度
  • 监控维护操作对系统性能的影响
  • 记录维护操作的结果
  • 分析维护操作的效果

4. 测试维护操作

  • 在测试环境中测试维护操作
  • 评估维护操作对查询性能的影响
  • 验证维护操作的正确性
  • 制定回滚计划

5. 自动化维护任务

  • 使用 Oracle 自动维护任务
  • 使用 shell 脚本或 PL/SQL 存储过程自动化维护任务
  • 使用调度工具(如 cron 或 Oracle Scheduler)定期执行维护任务

19c 和 21c 索引维护新特性

Oracle 19c 新特性

  1. 自动索引:Oracle 19c 引入了自动索引功能,能够自动识别、创建、维护和删除索引
  2. 索引重建增强:改进了索引重建算法,减少了索引重建的时间和资源消耗
  3. 统计信息收集增强:改进了统计信息收集算法,提高了统计信息的准确性和收集速度
  4. 分区索引维护增强:支持在线分区索引维护,允许在维护期间进行并发 DML 操作

Oracle 21c 新特性

  1. 智能索引维护:引入了机器学习算法,能够根据索引的使用情况自动调整维护策略
  2. 实时索引监控:提供实时的索引使用监控,能够及时识别需要维护的索引
  3. 索引压缩增强:改进了索引压缩算法,提高了压缩率和查询性能
  4. 索引碎片自动修复:能够自动识别和修复索引碎片
  5. 新视图 V$INDEX_MAINTENANCE:提供索引维护的实时监控信息

生产环境索引维护案例

案例 1:大型表索引重建

场景:一个包含 1 亿行数据的大型表,其索引碎片率超过 50%,查询性能明显下降。

解决方案

  1. 在线重建索引:使用 ONLINE 选项允许并发 DML 操作
  2. 使用并行度:设置适当的并行度提高重建速度
  3. 监控重建进度:使用 V$INDEX_REBUILD_STATUS 视图监控重建进度
  4. 验证重建效果:重建完成后,验证索引碎片率和查询性能
sql
-- 在线并行重建索引
ALTER INDEX SCOTT.IDX_LARGE_TABLE_COL REBUILD ONLINE PARALLEL 8;

-- 监控重建进度
SELECT * FROM V$INDEX_REBUILD_STATUS WHERE index_name = 'IDX_LARGE_TABLE_COL';

-- 恢复索引并行度
ALTER INDEX SCOTT.IDX_LARGE_TABLE_COL NOPARALLEL;

案例 2:分区表索引维护

场景:一个按月份分区的分区表,每月新增一个分区,需要定期维护分区索引。

解决方案

  1. 增量统计信息收集:只收集新增分区的统计信息
  2. 分区索引重建:只重建碎片化的分区索引
  3. 自动化维护:使用 Oracle Scheduler 自动执行维护任务
sql
-- 创建分区索引维护存储过程
CREATE OR REPLACE PROCEDURE maintain_partition_index IS
BEGIN
    -- 收集新增分区的统计信息
    EXEC DBMS_STATS.GATHER_INDEX_STATS(
        ownname => 'SCOTT',
        indname => 'IDX_SALES_DATE',
        partname => 'SALES_' || TO_CHAR(SYSDATE, 'MON_YYYY'),
        estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
        degree => DBMS_STATS.AUTO_DEGREE
    );
    
    -- 重建碎片化的分区索引
    FOR p IN (SELECT partition_name FROM dba_ind_partitions WHERE index_owner = 'SCOTT' AND index_name = 'IDX_SALES_DATE' AND num_rows > 1000000)
    LOOP
        BEGIN
            EXECUTE IMMEDIATE 'ALTER INDEX SCOTT.IDX_SALES_DATE REBUILD PARTITION ' || p.partition_name || ' ONLINE';
        EXCEPTION
            WHEN OTHERS THEN
                NULL;
        END;
    END LOOP;
END;
/

-- 创建调度任务
BEGIN
    DBMS_SCHEDULER.CREATE_JOB(
        job_name => 'MAINTAIN_PARTITION_INDEX_JOB',
        job_type => 'STORED_PROCEDURE',
        job_action => 'MAINTAIN_PARTITION_INDEX',
        start_date => SYSTIMESTAMP,
        repeat_interval => 'FREQ=MONTHLY; BYDAY=1; BYHOUR=2; BYMINUTE=0; BYSECOND=0',
        enabled => TRUE
    );
END;
/

案例 3:无用索引清理

场景:一个包含多个无用索引的数据库,需要识别和删除这些无用的索引。

解决方案

  1. 启用索引监控:为所有索引启用监控功能
  2. 收集监控数据:收集 30 天的索引监控数据
  3. 识别无用索引:根据监控数据识别无用的索引
  4. 验证无用索引:手动验证索引的使用情况
  5. 删除无用索引:删除确认无用的索引
sql
-- 为所有索引启用监控
DECLARE
    CURSOR c_indexes IS
        SELECT owner, index_name FROM dba_indexes WHERE owner NOT IN ('SYS', 'SYSTEM', 'SYSMAN', 'DBSNMP');
BEGIN
    FOR idx IN c_indexes LOOP
        BEGIN
            EXECUTE IMMEDIATE 'ALTER INDEX ' || idx.owner || '.' || idx.index_name || ' MONITORING USAGE';
        EXCEPTION
            WHEN OTHERS THEN
                NULL;
        END;
    END LOOP;
END;
/

-- 30 天后,查看无用的索引
SELECT
    owner,
    index_name,
    table_name
FROM
    dba_indexes
WHERE
    owner NOT IN ('SYS', 'SYSTEM', 'SYSMAN', 'DBSNMP')
    AND index_name NOT IN (
        SELECT DISTINCT index_name
        FROM v$object_usage
        WHERE used = 'YES'
    );

-- 删除无用的索引
DECLARE
    CURSOR c_unused_indexes IS
        SELECT owner, index_name
        FROM dba_indexes
        WHERE
            owner NOT IN ('SYS', 'SYSTEM', 'SYSMAN', 'DBSNMP')
            AND index_name NOT IN (
                SELECT DISTINCT index_name
                FROM v$object_usage
                WHERE used = 'YES'
            );
BEGIN
    FOR idx IN c_unused_indexes LOOP
        BEGIN
            EXECUTE IMMEDIATE 'DROP INDEX ' || idx.owner || '.' || idx.index_name;
        EXCEPTION
            WHEN OTHERS THEN
                NULL;
        END;
    END LOOP;
END;
/

常见问题 (FAQ)

如何判断索引是否需要重建?

判断索引是否需要重建的方法包括:

  • 检查索引碎片率:如果碎片率超过 30%,需要重建
  • 检查索引高度:如果索引高度超过 4 层,需要重建
  • 检查索引的叶子块使用率:如果使用率低于 50%,需要重建
  • 检查查询性能:如果查询性能明显下降,可能需要重建索引

在线重建索引和离线重建索引有什么区别?

  • 在线重建索引:允许并发 DML 操作,不阻塞用户访问,但需要更多的系统资源
  • 离线重建索引:不允许并发 DML 操作,会阻塞用户访问,但需要较少的系统资源

如何收集准确的索引统计信息?

收集准确的索引统计信息的方法包括:

  • 使用合适的采样率:建议使用 DBMS_STATS.AUTO_SAMPLE_SIZE
  • 使用并行度:提高统计信息收集的速度
  • 定期收集统计信息:根据表的变化频率确定收集频率
  • 使用增量统计信息收集:对于分区表,只收集新增或修改的分区的统计信息

如何监控索引的使用情况?

监控索引使用情况的方法包括:

  • 使用索引监控功能:ALTER INDEX ... MONITORING USAGE
  • 使用 AWR 报告:查看索引的使用统计信息
  • 使用 ASH 报告:查看索引的实时使用情况
  • 使用 OEM 或其他监控工具:提供可视化的索引使用监控

如何删除无用的索引?

删除无用的索引的步骤包括:

  1. 启用索引监控功能
  2. 收集足够的监控数据(建议 30 天)
  3. 根据监控数据识别无用的索引
  4. 手动验证索引的使用情况
  5. 删除确认无用的索引
  6. 监控删除索引后的系统性能

19c 和 21c 在索引维护方面有什么主要区别?

Oracle 21c 在索引维护方面相比 19c 有以下主要增强:

  • 引入了智能索引维护功能
  • 提供了实时索引监控
  • 改进了索引压缩算法
  • 支持索引碎片自动修复
  • 新增了 V$INDEX_MAINTENANCE 视图

如何自动化索引维护任务?

自动化索引维护任务的方法包括:

  • 使用 Oracle 自动维护任务
  • 使用 PL/SQL 存储过程编写维护脚本
  • 使用 Oracle Scheduler 或 cron 调度维护任务
  • 使用第三方工具,如 OEM 或 GoldenGate

索引维护对系统性能有什么影响?

索引维护对系统性能的影响包括:

  • 增加 CPU 和 I/O 消耗
  • 可能阻塞用户访问(离线维护)
  • 临时增加存储需求
  • 提高查询性能(长期影响)

总结

索引维护是 Oracle 数据库性能管理的重要组成部分,定期的索引维护能够确保索引的高效运行,提高查询性能,减少系统资源消耗。索引维护包括索引重建、索引统计收集、索引监控和索引优化等任务。

在进行索引维护时,DBA 应该考虑以下因素:

  • 选择合适的维护窗口
  • 优化维护操作
  • 监控维护操作
  • 测试维护操作
  • 自动化维护任务

随着 Oracle 版本的升级,索引维护功能不断增强,DBA 应充分利用这些新特性,提高索引维护的效率和效果。通过合理的索引维护,可以有效提高数据库系统的性能和可靠性,满足业务需求。