Skip to content

Oracle 索引设计原则

索引设计概述

索引是提高 Oracle 数据库查询性能的重要手段,合理的索引设计能够显著提高查询速度,减少 I/O 操作,降低系统资源消耗。然而,不恰当的索引设计不仅无法提高性能,反而会导致系统性能下降,增加维护成本。

索引设计是一个复杂的过程,需要考虑多种因素,包括:

  • 业务查询模式
  • 数据分布特征
  • 表的大小和增长趋势
  • DML 操作频率
  • 系统资源状况

索引基本概念

索引的类型

Oracle 支持多种索引类型,每种类型适用于不同的场景:

  1. B树索引:最常用的索引类型,适用于等值查询和范围查询
  2. 位图索引:适用于低基数列,如性别、状态等
  3. 函数索引:基于函数或表达式的索引,适用于频繁使用函数查询的场景
  4. 分区索引:与分区表关联的索引,适用于大型表
  5. 反向索引:将索引键值反转存储,适用于序列生成的主键
  6. 位图连接索引:基于多表连接的位图索引,适用于数据仓库环境
  7. 全文索引:用于文本搜索的索引
  8. 空间索引:用于地理空间数据的索引

索引的结构

B树索引是 Oracle 中最常用的索引类型,其结构如下:

  • 根节点:索引的顶层节点,包含指向分支节点的指针
  • 分支节点:中间层节点,包含索引键值和指向子节点的指针
  • 叶节点:底层节点,包含索引键值和指向表中数据行的指针
  • 双向链表:叶节点之间通过双向链表连接,便于范围查询

索引设计核心原则

1. 基于查询模式设计索引

索引设计的首要原则是基于实际的查询模式,而不是表结构。DBA 应该分析系统中的查询语句,识别频繁执行的查询,然后为这些查询设计合适的索引。

识别关键查询

sql
-- 查看最近 7 天执行频率最高的 SQL
SELECT
    sql_id,
    sql_text,
    executions,
    elapsed_time / executions / 1000000 AS avg_elapsed_seconds
FROM
    dba_hist_sqlstat
WHERE
    executions > 100
    AND snap_id IN (SELECT snap_id FROM dba_hist_snapshot WHERE begin_interval_time > SYSDATE - 7)
ORDER BY
    executions DESC;

分析查询的 WHERE 子句

为查询的 WHERE 子句中频繁使用的列创建索引,特别是那些具有高选择性的列。

2. 考虑列的选择性

列的选择性是指列中不同值的数量与总行数的比率。选择性越高,索引的效率越高。

  • 高选择性列:如主键、唯一键、身份证号等,适合创建 B 树索引
  • 低选择性列:如性别、状态等(不同值数量少于 10%),适合创建位图索引

计算列的选择性

sql
-- 计算列的选择性
SELECT
    column_name,
    num_distinct,
    num_rows,
    ROUND(num_distinct / num_rows, 4) AS selectivity
FROM
    dba_tab_col_statistics
WHERE
    owner = 'SCOTT'
    AND table_name = 'EMP';

3. 复合索引的列顺序

创建复合索引时,列的顺序至关重要,应遵循以下原则:

  1. 将选择性最高的列放在最前面
  2. 将频繁用于等值查询的列放在前面
  3. 将用于范围查询的列放在后面
  4. 考虑查询中列的使用频率

复合索引示例

sql
-- 假设查询频繁使用以下 WHERE 子句
-- WHERE department_id = 10 AND salary > 5000 AND hire_date > '01-JAN-2020'

-- 推荐的复合索引顺序
CREATE INDEX idx_emp_dept_sal_hire ON EMP(department_id, salary, hire_date);

4. 避免过多的索引

每个索引都会带来维护成本,特别是在频繁进行 DML 操作的表上。过多的索引会导致:

  • 插入、更新、删除操作变慢
  • 索引占用过多存储空间
  • 优化器选择执行计划的时间增加
  • 索引维护开销增加

一般来说,一个表的索引数量不应超过 5-7 个。

5. 考虑索引的存储开销

索引会占用存储空间,特别是对于大型表。DBA 应该考虑:

  • 索引的大小
  • 索引的增长趋势
  • 存储设备的容量和性能

估算索引大小

sql
-- 估算索引大小
SELECT
    index_name,
    ROUND(bytes / 1024 / 1024, 2) AS index_size_mb
FROM
    dba_segments
WHERE
    owner = 'SCOTT'
    AND segment_type = 'INDEX';

6. 为外键创建索引

为外键列创建索引可以提高以下操作的性能:

  • 从父表删除或更新主键时
  • 子表与父表的连接查询
  • 子表的外键列查询

此外,为外键创建索引还可以避免锁升级,防止死锁。

7. 考虑索引的使用方式

索引的使用方式主要有以下几种:

  1. 访问路径

    • 索引唯一扫描(INDEX UNIQUE SCAN):用于主键或唯一索引的等值查询
    • 索引范围扫描(INDEX RANGE SCAN):用于范围查询
    • 索引全扫描(INDEX FULL SCAN):用于查询所有索引列的情况
    • 索引快速全扫描(INDEX FAST FULL SCAN):使用多块读的方式扫描整个索引
    • 索引跳跃扫描(INDEX SKIP SCAN):用于复合索引中前导列未被使用的情况
  2. 连接方式

    • 嵌套循环连接(NESTED LOOPS):适合小结果集的连接
    • 哈希连接(HASH JOIN):适合大结果集的连接
    • 排序合并连接(SORT MERGE JOIN):适合有序数据的连接

8. 定期评估索引的有效性

DBA 应该定期评估索引的有效性,识别并删除无用的索引。

识别无用的索引

sql
-- 查看最近 7 天未使用的索引
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 - 7)
    );

索引设计步骤

1. 收集需求

  • 了解业务查询模式
  • 识别核心业务流程
  • 分析数据分布特征
  • 确定表的大小和增长趋势

2. 分析查询语句

  • 收集系统中的查询语句
  • 识别频繁执行的查询
  • 分析查询的 WHERE 子句和连接条件
  • 分析查询的排序和分组需求

3. 设计索引方案

  • 为频繁查询的列创建索引
  • 设计复合索引的列顺序
  • 选择合适的索引类型
  • 考虑索引的存储位置

4. 评估索引方案

  • 估算索引的存储空间
  • 评估索引对 DML 操作的影响
  • 使用 EXPLAIN PLAN 分析查询执行计划
  • 进行性能测试

5. 实施和监控

  • 创建索引
  • 监控索引的使用情况
  • 定期收集统计信息
  • 评估索引的有效性

不同场景的索引设计

1. 在线事务处理 (OLTP) 系统

OLTP 系统的特点是频繁的 DML 操作和简单的查询,索引设计应遵循以下原则:

  • 优先考虑主键和唯一键索引
  • 为频繁查询的列创建 B 树索引
  • 限制索引数量,一般不超过 5 个
  • 避免使用位图索引
  • 为外键创建索引

2. 数据仓库 (DW) 系统

数据仓库系统的特点是大量的数据加载和复杂的查询,索引设计应遵循以下原则:

  • 优先考虑位图索引和位图连接索引
  • 为维度表的主键创建 B 树索引
  • 为事实表的外键创建位图索引
  • 考虑使用分区索引
  • 为频繁用于过滤和分组的列创建索引

3. 混合系统

混合系统同时包含 OLTP 和数据仓库的特点,索引设计应根据具体的业务需求进行权衡:

  • 对于 OLTP 部分,采用 OLTP 系统的索引设计原则
  • 对于数据仓库部分,采用数据仓库系统的索引设计原则
  • 考虑使用分区表和分区索引,分离冷热数据

索引设计最佳实践

1. 优先使用 B 树索引

B 树索引是 Oracle 中最常用的索引类型,适用于大多数场景。只有在特定情况下才考虑使用其他类型的索引。

2. 合理设计复合索引

  • 复合索引的列数一般不超过 4 个
  • 将选择性最高的列放在最前面
  • 考虑查询中列的使用顺序
  • 避免冗余索引

3. 避免在频繁更新的列上创建索引

频繁更新的列会导致索引频繁维护,增加系统开销。如果必须在这些列上创建索引,应考虑以下措施:

  • 减少索引数量
  • 使用批量更新操作
  • 定期重建索引

4. 考虑索引的存储位置

  • 将索引和表存储在不同的表空间中,减少 I/O 争用
  • 对于大型索引,考虑使用单独的表空间
  • 对于频繁访问的索引,存储在高性能存储上

5. 定期收集统计信息

准确的统计信息对于优化器选择正确的执行计划至关重要。DBA 应该定期收集表和索引的统计信息:

sql
-- 收集表和索引的统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS(
    ownname => 'SCOTT',
    tabname => 'EMP',
    cascade => TRUE,
    estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE
);

6. 考虑使用索引压缩

索引压缩可以减少索引占用的存储空间,提高 I/O 性能。Oracle 支持以下索引压缩方式:

  • 前缀压缩:压缩索引键值的前缀部分
  • 高级索引压缩:压缩整个索引键值
sql
-- 创建压缩索引
CREATE INDEX idx_emp_dept ON EMP(department_id) COMPRESS;

-- 创建高级压缩索引
CREATE INDEX idx_emp_dept_sal ON EMP(department_id, salary) COMPRESS ADVANCED;

7. 避免使用索引的情况

在以下情况下,应避免使用索引:

  • 表的行数较少(一般少于 1000 行)
  • 查询需要返回表中大部分数据(超过 20%)
  • 频繁进行全表扫描的表
  • 临时表

19c 和 21c 索引设计新特性

Oracle 19c 新特性

  1. 自动索引:Oracle 19c 引入了自动索引功能,能够自动识别和创建索引,减少人工干预
  2. 索引优化建议:通过 SQL Tuning Advisor 提供更准确的索引优化建议
  3. 索引重建增强:改进了索引重建算法,减少了索引重建的时间和资源消耗
  4. 分区索引增强:支持在线分区索引维护

Oracle 21c 新特性

  1. 实时索引监控:提供实时的索引使用监控,能够及时识别无用的索引
  2. 智能索引设计:引入了机器学习算法,能够根据查询模式自动设计最优索引
  3. 索引压缩增强:改进了索引压缩算法,提高了压缩率
  4. 新索引类型:引入了一些新的索引类型,如 JSON 索引、区块链表索引等

生产环境索引管理

1. 索引创建策略

  • 在业务低峰期创建索引
  • 对于大型表,使用 ONLINE 选项创建索引
  • 监控索引创建过程,确保不会影响系统性能
sql
-- 在线创建索引
CREATE INDEX idx_emp_dept ON EMP(department_id) ONLINE;

2. 索引重建策略

  • 定期重建膨胀的索引
  • 重建索引前,确保有有效的备份
  • 对于大型索引,使用 ONLINE 选项重建
sql
-- 在线重建索引
ALTER INDEX idx_emp_dept REBUILD ONLINE;

3. 索引监控

  • 监控索引的使用率
  • 监控索引的膨胀情况
  • 监控索引对 DML 操作的影响
  • 设置索引相关的告警

4. 索引迁移策略

  • 当表结构发生变化时,评估索引的有效性
  • 当查询模式发生变化时,调整索引设计
  • 定期审查索引设计,确保符合业务需求

常见问题 (FAQ)

如何确定是否需要创建索引?

确定是否需要创建索引的方法包括:

  • 分析查询的执行计划,查看是否存在全表扫描
  • 监控查询的响应时间,判断是否需要优化
  • 分析表的大小和增长趋势
  • 考虑 DML 操作的频率

复合索引和多个单列索引有什么区别?

  • 复合索引:多个列组合成一个索引,适用于同时使用这些列的查询
  • 多个单列索引:每个列单独创建索引,Oracle 会选择其中一个或多个索引进行合并

复合索引的优点是查询效率更高,维护成本更低;缺点是不够灵活,只适用于特定的查询模式。

如何选择复合索引的列顺序?

选择复合索引的列顺序应考虑以下因素:

  • 列的选择性,选择性高的列放在前面
  • 列在查询中的使用频率,频繁使用的列放在前面
  • 列的查询类型,等值查询的列放在前面,范围查询的列放在后面

什么时候使用位图索引?

位图索引适用于以下情况:

  • 低基数列(不同值数量少于 10%)
  • 数据仓库环境
  • 只读或很少更新的表
  • 频繁用于 AND、OR、IN 查询的列

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

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

  • 查询 V$SQL_PLAN 视图,查看索引的使用情况
  • 查询 DBA_HIST_SQL_PLAN 视图,查看历史索引使用情况
  • 使用 Oracle Enterprise Manager 监控索引使用率
  • 启用索引监控功能
sql
-- 启用索引监控
ALTER INDEX idx_emp_dept MONITORING USAGE;

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

-- 禁用索引监控
ALTER INDEX idx_emp_dept NOMONITORING USAGE;

如何处理索引碎片?

处理索引碎片的方法包括:

  • 重建索引
  • 合并索引(使用 ALTER INDEX COALESCE)
  • 收缩索引(对于分区索引)

总结

索引设计是 Oracle 数据库性能优化的重要组成部分,合理的索引设计能够显著提高查询性能。DBA 应该基于业务查询模式,考虑列的选择性、复合索引的列顺序、索引的维护成本等因素,设计出最优的索引方案。

在索引设计过程中,DBA 应该遵循核心原则,根据不同的系统类型(OLTP、数据仓库)选择合适的索引类型,定期评估索引的有效性,确保索引能够真正提高系统性能。

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