Oracle 索引优化策略

Oracle 索引优化策略

适用版本:Oracle Database 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07


1. 概述

索引是查询性能优化的关键[1]:

目标

  • 减少全表扫描
  • 提升 WHERE/JOIN/ORDER BY 性能
  • 平衡查询与 DML 性能

2. 索引选择

2.1 决策矩阵

场景推荐索引
高基数等值B-Tree
低基数Bitmap
序列生成Reverse Key
函数查询Function-Based
主键查询IOT
范围查询B-Tree
OLAPBitmap
OLTPB-Tree

2.2 候选列

  • WHERE 条件列
  • JOIN 连接列
  • ORDER BY 列
  • GROUP BY 列
  • 高选择性列

3. 复合索引

3.1 列顺序原则

  • 等值条件在前
  • 范围条件在后
  • 高选择性在前
  • 排序列考虑

3.2 示例

-- 查询:WHERE dept_id = 10 AND salary > 5000
CREATE INDEX idx_emp_dept_sal ON employees(dept_id, salary);

-- 查询:WHERE name = 'Smith' ORDER BY create_date
CREATE INDEX idx_emp_name_date ON employees(name, create_date);

3.3 前缀扫描

-- 索引 (a, b, c)
SELECT * FROM t WHERE a = 1;              -- 使用
SELECT * FROM t WHERE a = 1 AND b = 2;    -- 使用
SELECT * FROM t WHERE a = 1 AND b = 2 AND c = 3;  -- 使用
SELECT * FROM t WHERE b = 2;              -- 不使用
SELECT * FROM t WHERE a = 1 AND c = 3;    -- 部分使用

4. Function-Based 索引

4.1 适用

  • WHERE 使用函数
  • 隐式转换
  • 大小写查询

4.2 创建

-- 大小写
CREATE INDEX idx_emp_upper ON employees(UPPER(last_name));

-- 表达式
CREATE INDEX idx_emp_annual ON employees(salary * 12);

-- 复杂
CREATE INDEX idx_emp_comp ON employees(dept_id || '_' || job_id);

-- 使用
SELECT * FROM employees WHERE UPPER(last_name) = 'SMITH';
SELECT * FROM employees WHERE salary * 12 > 60000;

5. Bitmap 索引

5.1 适用

  • 低基数列(< 1%)
  • OLAP/DSS
  • 多列 AND/OR

5.2 创建

CREATE BITMAP INDEX idx_emp_gender ON employees(gender);
CREATE BITMAP INDEX idx_emp_status ON employees(status);
CREATE BITMAP INDEX idx_emp_married ON employees(married);

5.3 限制

  • 不适合 OLTP
  • DML 开销大
  • 锁全索引

6. 索引维护

6.1 重建

ALTER INDEX idx_emp_name REBUILD;
ALTER INDEX idx_emp_name REBUILD ONLINE;
ALTER INDEX idx_emp_name REBUILD TABLESPACE indx;

6.2 合并

ALTER INDEX idx_emp_name COALESCE;

6.3 统计信息

EXEC DBMS_STATS.GATHER_INDEX_STATS('SCOTT', 'idx_emp_name');

6.4 监控使用

ALTER INDEX idx_emp_name MONITORING USAGE;
SELECT * FROM v$object_usage;
ALTER INDEX idx_emp_name NOMONITORING USAGE;

7. 索引失效场景

7.1 函数阻止

-- 索引 name
WHERE UPPER(name) = 'X'  -- 不用索引

-- 解决:函数索引
CREATE INDEX idx_upper ON t(UPPER(name));

7.2 隐式转换

-- 列类型 NUMBER,查询字符
WHERE id = '100'  -- 不用索引

-- 解决
WHERE id = 100

7.3 NULL 查询

WHERE name IS NULL  -- 不用索引

-- 解决:默认值 或 Bitmap 索引

7.4 LIKE

WHERE name LIKE '%ith'  -- 不用索引

-- 解决
WHERE name LIKE 'Smit%'  -- 使用

7.5 负向条件

WHERE id != 10  -- 不用索引

-- 解决
WHERE id < 10 OR id > 10

8. 索引分析

8.1 查看索引

SELECT 
  index_name,
  index_type,
  uniqueness,
  status,
  blevel,
  leaf_blocks,
  distinct_keys,
  clustering_factor
FROM user_indexes
WHERE table_name = 'EMPLOYEES';

8.2 聚簇因子

-- CF 接近块数:索引有序
-- CF 接近行数:索引无序
SELECT index_name, clustering_factor, num_rows, leaf_blocks
FROM user_indexes
WHERE table_name = 'EMPLOYEES';

8.3 索引高度

ANALYZE INDEX idx_emp_name VALIDATE STRUCTURE;
SELECT height, name, lf_rows, del_lf_rows FROM index_stats;
-- height > 4 重建
-- del_lf_rows / lf_rows > 20% 重建

9. 索引压缩

9.1 前缀压缩

CREATE INDEX idx_emp ON employees(dept_id, dept_name) COMPRESS 1;
-- 节省空间,适合重复值多

9.2 重建压缩

ALTER INDEX idx_emp REBUILD COMPRESS 1;

10. Invisible 索引

10.1 创建

CREATE INDEX idx_test ON employees(test_col) INVISIBLE;

10.2 测试

-- 优化器忽略
ALTER SESSION SET optimizer_use_invisible_indexes = TRUE;
-- 测试后决定保留或删除

11. 索引监控

11.1 v$object_usage

SELECT 
  index_name,
  table_name,
  monitoring,
  used,
  start_monitoring,
  end_monitoring
FROM v$object_usage;

11.2 AWR

SELECT * FROM dba_hist_sql_plan
WHERE operation = 'INDEX'
  AND object_name = 'IDX_EMP_NAME';

12. 常见坑与排错

12.1 索引不使用

-- 1. 检查统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS(...);

-- 2. 检查 WHERE 条件
-- 3. 检查 HINT
-- 4. 检查参数

12.2 索引碎片

-- 重建
ALTER INDEX idx_name REBUILD ONLINE;

12.3 索引失效

-- 状态 UNUSABLE
ALTER INDEX idx_name REBUILD;

12.4 DML 慢

-- 索引过多
-- 1. 监控使用
-- 2. 删除无用索引

13. 最佳实践

  1. 主键自动索引:默认
  2. 外键加索引:避免锁
  3. WHERE/JOIN/ORDER 列建索引:性能
  4. 复合索引列顺序:等值在前
  5. 函数列用 FBI:避免全表扫描
  6. 低基数用 Bitmap:OLAP
  7. 定期重建:减少碎片
  8. 监控使用:删除无用
  9. Invisible 测试:安全
  10. 避免过度索引:影响 DML

14. 参考资料

[1] Oracle Database Performance Tuning Guide 19c, “Indexing” https://docs.oracle.com/en/database/oracle/oracle-database/19/tgsql/indexing.html