外观
Oracle 索引管理
索引类型
B树索引
特点
- 结构:平衡树结构,适合范围查询和精确查询
- 适用场景:高基数列(如主键、唯一约束)
- 存储:存储索引键值和行ID
- 查询性能:适用于大多数查询场景
创建语法
sql
-- 创建普通B树索引
CREATE INDEX emp_last_name_idx ON employees(last_name);
-- 创建唯一B树索引
CREATE UNIQUE INDEX emp_email_idx ON employees(email);
-- 创建复合B树索引
CREATE INDEX emp_dept_job_idx ON employees(department_id, job_id);
-- 创建反向键索引(减少索引叶节点争用)
CREATE INDEX emp_empno_idx ON employees(employee_id) REVERSE;最佳实践
- 为频繁查询的列创建索引
- 为高基数列创建索引
- 考虑创建复合索引覆盖常用查询
- 避免在经常更新的列上创建过多索引
位图索引
特点
- 结构:使用位图存储索引键值和行ID的映射
- 适用场景:低基数列(如性别、状态等)
- 存储:存储空间小,适合列值重复率高的情况
- 查询性能:适用于复杂的AND/OR查询,不适合高并发更新
创建语法
sql
-- 创建位图索引
CREATE BITMAP INDEX emp_gender_idx ON employees(gender);
-- 创建复合位图索引
CREATE BITMAP INDEX emp_dept_status_idx ON employees(department_id, status);最佳实践
- 仅在低基数列上使用位图索引
- 避免在频繁更新的列上使用位图索引
- 适用于数据仓库和OLAP系统
- 可与B树索引结合使用
函数索引
特点
- 结构:基于函数或表达式的计算结果创建索引
- 适用场景:频繁使用函数或表达式的查询
- 存储:存储函数计算结果和行ID
- 查询性能:加速包含函数的查询
创建语法
sql
-- 创建基于函数的索引
CREATE INDEX emp_hire_year_idx ON employees(EXTRACT(YEAR FROM hire_date));
-- 创建基于表达式的索引
CREATE INDEX emp_salary_range_idx ON employees(CASE WHEN salary > 5000 THEN 'HIGH' ELSE 'LOW' END);
-- 创建基于UPPER函数的索引
CREATE INDEX emp_last_name_upper_idx ON employees(UPPER(last_name));最佳实践
- 为频繁使用的函数或表达式创建函数索引
- 注意函数索引的维护成本
- 确保函数是确定性的
- 考虑函数索引对DML操作的影响
分区索引
特点
- 结构:与分区表对应,分为本地分区索引和全局分区索引
- 适用场景:分区表的索引
- 存储:本地分区索引与表分区一一对应
- 查询性能:支持分区剪枝,提高查询性能
创建语法
sql
-- 创建本地分区索引
CREATE INDEX sales_product_idx ON sales(product_id) LOCAL;
-- 创建本地前缀分区索引
CREATE INDEX sales_date_idx ON sales(sale_date) LOCAL (
PARTITION sales_q1_2023,
PARTITION sales_q2_2023,
PARTITION sales_q3_2023,
PARTITION sales_q4_2023
);
-- 创建全局分区索引
CREATE INDEX sales_customer_idx ON sales(customer_id) GLOBAL
PARTITION BY RANGE (customer_id) (
PARTITION p1 VALUES LESS THAN (1000),
PARTITION p2 VALUES LESS THAN (2000),
PARTITION p3 VALUES LESS THAN (MAXVALUE)
);最佳实践
- 对于分区表,优先使用本地分区索引
- 本地分区索引维护成本低,支持分区独立性
- 全局分区索引适合跨分区查询
- 考虑分区索引的重建策略
其他索引类型
文本索引
- 特点:用于全文搜索
- 适用场景:包含大量文本数据的列
- 创建语法:使用CREATE INDEX ... INDEXTYPE IS CTXSYS.CONTEXT
空间索引
- 特点:用于地理空间数据
- 适用场景:存储地理位置数据的列
- 创建语法:使用CREATE INDEX ... INDEXTYPE IS MDSYS.SPATIAL_INDEX
域索引
- 特点:用户自定义索引类型
- 适用场景:特殊数据类型的索引
- 创建语法:使用CREATE INDEX ... INDEXTYPE IS 自定义索引类型
索引创建和管理
索引创建
基本语法
sql
-- 基本索引创建
CREATE [UNIQUE] [BITMAP] INDEX index_name
ON table_name (column1 [ASC | DESC], column2 [ASC | DESC], ...)
[TABLESPACE tablespace_name]
[PCTFREE n]
[STORAGE (storage_clause)]
[NOLOGGING]
[COMPRESS [n]]
[REVERSE]
[LOCAL | GLOBAL PARTITION BY ...];创建选项
- UNIQUE:创建唯一索引
- BITMAP:创建位图索引
- TABLESPACE:指定索引存储的表空间
- PCTFREE:指定索引块中保留的空间百分比
- STORAGE:指定索引的存储参数
- NOLOGGING:创建索引时不生成重做日志
- COMPRESS:启用索引压缩
- REVERSE:创建反向键索引
- LOCAL/GLOBAL:指定分区索引类型
示例
sql
-- 创建带存储参数的索引
CREATE INDEX emp_salary_idx ON employees(salary)
TABLESPACE index_ts
PCTFREE 10
STORAGE (
INITIAL 64K
NEXT 128K
MAXEXTENTS UNLIMITED
PCTINCREASE 0
)
NOLOGGING
COMPRESS 1;索引修改
重命名索引
sql
-- 重命名索引
ALTER INDEX emp_last_name_idx RENAME TO emp_ln_idx;重建索引
sql
-- 重建索引
ALTER INDEX emp_email_idx REBUILD;
-- 在线重建索引(不阻塞DML操作)
ALTER INDEX emp_email_idx REBUILD ONLINE;
-- 重建索引并修改存储参数
ALTER INDEX emp_email_idx REBUILD
TABLESPACE index_ts
PCTFREE 10
STORAGE (
INITIAL 128K
NEXT 256K
);
-- 重建分区索引的特定分区
ALTER INDEX sales_product_idx REBUILD PARTITION sales_q1_2023;修改索引参数
sql
-- 修改索引的存储参数
ALTER INDEX emp_salary_idx STORAGE (NEXT 256K);
-- 启用或禁用索引
ALTER INDEX emp_salary_idx ENABLE;
ALTER INDEX emp_salary_idx DISABLE;
-- 合并索引碎片
ALTER INDEX emp_salary_idx COALESCE;索引删除
删除索引
sql
-- 删除索引
DROP INDEX emp_salary_idx;
-- 删除分区索引的特定分区
ALTER INDEX sales_product_idx DROP PARTITION sales_q1_2023;注意事项
- 删除索引前确认不再需要该索引
- 删除索引会影响依赖该索引的查询性能
- 删除索引不会影响表数据
- 定期清理无用的索引,减少维护成本
索引性能优化
索引设计优化
列选择
- 高频查询列:为频繁出现在WHERE子句中的列创建索引
- 高基数列:优先为高基数列创建索引
- 连接列:为频繁用于表连接的列创建索引
- 排序分组列:为频繁用于ORDER BY和GROUP BY的列创建索引
复合索引设计
- 列顺序:将选择性高的列放在前面
- 覆盖查询:设计复合索引覆盖常用查询的所有列
- 前缀使用:复合索引的前缀列应适合常用查询模式
- 避免冗余:避免创建与现有索引前缀相同的索引
示例
sql
-- 好的复合索引设计(选择性高的列在前)
CREATE INDEX emp_dept_salary_idx ON employees(department_id, salary);
-- 覆盖查询的复合索引
CREATE INDEX emp_dept_job_salary_idx ON employees(department_id, job_id, salary);
-- 避免冗余索引
-- 如果已有索引 (a, b),则不需要单独创建索引 (a)索引使用优化
查询优化
- 使用索引列:确保查询条件使用索引列
- 避免函数:避免在索引列上使用函数
- 使用绑定变量:使用绑定变量,减少硬解析
- 避免隐式类型转换:避免索引列的隐式类型转换
示例
sql
-- 不使用函数在索引列上
-- 不好的查询
SELECT * FROM employees WHERE UPPER(last_name) = 'SMITH';
-- 好的查询(使用函数索引)
CREATE INDEX emp_last_name_upper_idx ON employees(UPPER(last_name));
SELECT * FROM employees WHERE UPPER(last_name) = 'SMITH';
-- 避免隐式类型转换
-- 不好的查询(会导致全表扫描)
SELECT * FROM employees WHERE employee_id = '100';
-- 好的查询
SELECT * FROM employees WHERE employee_id = 100;索引提示
- 使用索引提示:在必要时使用索引提示引导优化器
- 避免过度使用:仅在优化器选择错误时使用提示
- 测试验证:使用提示后验证性能改进
示例
sql
-- 使用索引提示
SELECT /*+ INDEX(employees emp_last_name_idx) */ *
FROM employees
WHERE last_name = 'Smith';
-- 使用索引范围扫描提示
SELECT /*+ INDEX_RANGE_SCAN(employees emp_dept_job_idx) */ *
FROM employees
WHERE department_id = 10 AND job_id = 'IT_PROG';索引维护
索引碎片整理
- 重建索引:使用ALTER INDEX REBUILD整理碎片
- 合并索引:使用ALTER INDEX COALESCE合并相邻的空闲空间
- 在线操作:使用ONLINE选项减少对用户的影响
示例
sql
-- 重建索引整理碎片
ALTER INDEX emp_last_name_idx REBUILD ONLINE;
-- 合并索引碎片
ALTER INDEX emp_last_name_idx COALESCE;索引统计信息
- 收集统计信息:定期收集索引统计信息
- 自动统计信息:启用自动统计信息收集
- 手动收集:对于大型索引手动收集统计信息
示例
sql
-- 收集表和索引的统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'HR',
tabname => 'EMPLOYEES',
cascade => TRUE,
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
method_opt => 'FOR ALL COLUMNS SIZE AUTO'
);
-- 仅收集索引的统计信息
EXEC DBMS_STATS.GATHER_INDEX_STATS(
ownname => 'HR',
indname => 'EMP_LAST_NAME_IDX',
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE
);索引监控
- 监控使用情况:使用ALTER INDEX ... MONITORING USAGE
- 识别未使用索引:定期检查未使用的索引
- 优化索引结构:根据使用情况调整索引
示例
sql
-- 开始监控索引使用
ALTER INDEX emp_last_name_idx MONITORING USAGE;
-- 查看索引使用情况
SELECT index_name, table_name, monitoring, used
FROM v$object_usage
WHERE index_name = 'EMP_LAST_NAME_IDX';
-- 停止监控索引使用
ALTER INDEX emp_last_name_idx NOMONITORING USAGE;索引性能分析
索引访问路径
全表扫描 (FTS)
- 适用场景:小表或需要大部分行的查询
- 成本:读取所有数据块
- 优化:对于大表,考虑使用索引扫描
索引唯一扫描 (INDEX UNIQUE SCAN)
- 适用场景:基于唯一索引的精确查询
- 成本:读取索引的单个叶节点
- 优势:性能最佳,结果唯一
索引范围扫描 (INDEX RANGE SCAN)
- 适用场景:基于索引的范围查询
- 成本:读取索引的连续叶节点
- 优势:适合范围查询和排序操作
索引全扫描 (INDEX FULL SCAN)
- 适用场景:需要索引中所有列的查询
- 成本:读取整个索引
- 优势:避免回表操作
索引快速全扫描 (INDEX FAST FULL SCAN)
- 适用场景:需要索引中所有列的查询,支持并行
- 成本:读取整个索引,无序
- 优势:并行执行,性能好
索引跳跃扫描 (INDEX SKIP SCAN)
- 适用场景:复合索引,前缀列选择性低
- 成本:跳过前缀列的不同值
- 优势:在特定情况下避免全表扫描
执行计划分析
查看执行计划
sql
-- 使用EXPLAIN PLAN
EXPLAIN PLAN FOR
SELECT * FROM employees WHERE last_name = 'Smith';
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 使用AUTOTRACE
SET AUTOTRACE ON
SELECT * FROM employees WHERE last_name = 'Smith';
-- 使用SQL Developer或TOAD的执行计划工具执行计划解读
- 访问路径:全表扫描、索引扫描等
- 连接方式:嵌套循环、哈希连接、排序合并连接
- 成本:优化器估算的执行成本
- ** cardinality**:优化器估算的结果行数
- 字节:优化器估算的结果大小
示例执行计划
索引性能问题诊断
常见问题
- 索引缺失:需要索引的列没有索引
- 索引失效:索引存在但未被使用
- 索引过度:过多索引影响DML性能
- 索引碎片:索引碎片导致性能下降
- 统计信息过时:统计信息不准确导致优化器选择错误
诊断方法
- V$SQL视图:分析SQL执行情况
- AWR报告:分析数据库整体性能
- ASH报告:分析活动会话历史
- 索引监控:监控索引使用情况
- 执行计划分析:分析SQL执行计划
示例查询
sql
-- 查找使用全表扫描的SQL
SELECT sql_id, sql_text, executions, buffer_gets
FROM v$sql
WHERE buffer_gets > 10000
AND sql_text LIKE '%EMPLOYEES%'
ORDER BY buffer_gets DESC;
-- 查找未使用的索引
SELECT owner, index_name, table_name
FROM dba_indexes i
WHERE NOT EXISTS (
SELECT 1 FROM dba_objects o
WHERE o.object_name = i.index_name
AND o.object_type = 'INDEX'
AND o.status = 'VALID'
)
AND i.owner NOT IN ('SYS', 'SYSTEM');索引优化策略
索引生命周期管理
创建策略
- 需求分析:基于查询需求和性能分析创建索引
- 测试验证:在测试环境验证索引效果
- 渐进部署:在生产环境逐步部署索引
维护策略
- 定期重建:定期重建碎片化的索引
- 统计信息更新:定期更新索引统计信息
- 使用监控:监控索引使用情况
- 碎片整理:定期整理索引碎片
删除策略
- 未使用索引:删除长期未使用的索引
- 冗余索引:删除冗余的索引
- 性能影响:评估删除索引对查询性能的影响
- 备份:删除索引前确保有备份
分区表索引策略
本地分区索引
- 优势:与表分区一一对应,维护成本低
- 适用场景:大多数分区表索引
- 特点:分区独立性,支持分区级操作
全局分区索引
- 优势:适合跨分区查询
- 适用场景:需要全局唯一性或跨分区范围查询
- 特点:维护成本高,分区操作会影响整个索引
分区索引选择
- OLTP系统:优先使用本地分区索引
- 数据仓库:根据查询模式选择合适的分区索引
- 混合场景:结合使用本地和全局分区索引
索引压缩
压缩优势
- 减少存储:减少索引存储空间
- 提高缓存命中率:相同缓存可存储更多索引数据
- 减少I/O:减少索引扫描的I/O操作
压缩类型
- 前缀压缩:仅压缩索引键的前缀部分
- 完全压缩:压缩整个索引键
压缩级别
- 基本压缩:适合大多数索引
- 高级压缩:适合重复率高的索引
示例
sql
-- 创建压缩索引
CREATE INDEX emp_dept_job_idx ON employees(department_id, job_id) COMPRESS;
-- 指定压缩前缀长度
CREATE INDEX emp_dept_job_salary_idx ON employees(department_id, job_id, salary) COMPRESS 2;
-- 重建索引时启用压缩
ALTER INDEX emp_last_name_idx REBUILD COMPRESS;并行索引操作
并行创建
- 优势:加速索引创建过程
- 适用场景:大型表的索引创建
- 示例:
sql
-- 并行创建索引
CREATE INDEX emp_salary_idx ON employees(salary) PARALLEL 4;
-- 创建后恢复为串行
ALTER INDEX emp_salary_idx NOPARALLEL;并行重建
- 优势:加速索引重建过程
- 适用场景:大型索引的重建
- 示例:
sql
-- 并行重建索引
ALTER INDEX emp_last_name_idx REBUILD PARALLEL 4;
-- 在线并行重建索引
ALTER INDEX emp_last_name_idx REBUILD ONLINE PARALLEL 4;并行度选择
- CPU核心数:考虑服务器CPU核心数
- I/O能力:考虑存储系统I/O能力
- 系统负载:考虑系统当前负载
- 测试验证:通过测试确定最佳并行度
版本差异考虑
Oracle 11g
- 索引特性:支持基本索引类型和功能
- 压缩:支持基本索引压缩
- 并行操作:支持基本的并行索引操作
- 最佳实践:使用B树索引和位图索引,定期重建索引
Oracle 12c
- 索引特性:增强了索引功能,支持不可见索引
- 不可见索引:可以创建不可见索引,不影响优化器
- 部分索引:支持部分索引(12c R2+)
- 最佳实践:使用不可见索引进行测试,利用新的索引特性
Oracle 19c
- 索引特性:进一步增强了索引功能,支持自动索引
- 自动索引:自动创建、管理和删除索引
- 索引增强:增强了索引压缩和并行操作
- 最佳实践:利用自动索引功能,减少手动索引管理
Oracle 21c
- 索引特性:引入了更多智能化的索引功能
- 智能索引:支持机器学习辅助的索引优化
- 索引增强:增强了索引的并行处理能力
- 最佳实践:利用智能化索引功能,关注索引的自动化管理
常见问题(FAQ)
Q1: 如何确定是否需要创建索引?
A1: 确定是否需要创建索引的方法:
- 分析查询:分析频繁执行的查询,识别使用的列
- 性能测试:测试查询性能,确定是否需要索引
- 执行计划:查看执行计划,识别全表扫描
- 工作量分析:分析系统工作量,识别性能瓶颈
- 成本效益:评估索引对查询性能的提升与对DML性能的影响
Q2: 如何选择复合索引的列顺序?
A2: 选择复合索引列顺序的方法:
- 选择性:将选择性高的列放在前面
- 查询模式:根据常用查询模式确定列顺序
- 前缀使用:确保复合索引的前缀列适合常用查询
- 覆盖查询:考虑创建覆盖常用查询的复合索引
- 测试验证:通过测试验证复合索引的性能
Q3: 如何识别和处理未使用的索引?
A3: 识别和处理未使用索引的方法:
- 索引监控:使用ALTER INDEX ... MONITORING USAGE
- V$OBJECT_USAGE:查询v$object_usage视图
- AWR报告:分析AWR报告中的索引使用情况
- 删除策略:删除长期未使用的索引
- 测试验证:删除索引前测试对查询性能的影响
Q4: 如何处理索引碎片?
A4: 处理索引碎片的方法:
- 重建索引:使用ALTER INDEX ... REBUILD
- 在线重建:使用ALTER INDEX ... REBUILD ONLINE减少影响
- 合并索引:使用ALTER INDEX ... COALESCE
- 定期维护:建立索引维护计划,定期处理碎片
- 监控碎片:监控索引碎片情况,及时处理
Q5: 如何优化索引以提高查询性能?
A5: 优化索引提高查询性能的方法:
- 合适的索引类型:选择合适的索引类型
- 复合索引设计:合理设计复合索引
- 覆盖查询:设计覆盖常用查询的索引
- 查询优化:优化查询以使用索引
- 统计信息:保持索引统计信息的准确性
- 碎片管理:定期处理索引碎片
Q6: 如何平衡索引对查询和DML性能的影响?
A6: 平衡索引对查询和DML性能影响的方法:
- 需求分析:仅为必要的查询创建索引
- 选择性评估:评估索引对DML性能的影响
- 测试验证:测试索引对DML性能的影响
- 索引数量:控制表上的索引数量
- 分区策略:使用分区表和分区索引
- 在线操作:使用在线索引操作减少影响
Q7: 如何使用函数索引?
A7: 使用函数索引的方法:
- 识别需求:识别频繁使用函数的查询
- 创建函数索引:基于函数或表达式创建索引
- 查询优化:确保查询使用函数索引
- 维护成本:考虑函数索引的维护成本
- 测试验证:测试函数索引的性能效果
Q8: 如何处理索引导致的死锁?
A8: 处理索引导致死锁的方法:
- 索引设计:避免在高频更新的列上创建过多索引
- 并发控制:优化应用程序的并发控制
- 事务设计:减少事务持有锁的时间
- 索引顺序:对于复合索引,考虑列顺序对锁的影响
- 监控死锁:监控死锁情况,分析原因
Q9: 如何在分区表上选择合适的索引策略?
A9: 在分区表上选择合适索引策略的方法:
- 本地分区索引:优先使用本地分区索引
- 全局分区索引:仅在需要时使用全局分区索引
- 查询模式:根据查询模式选择索引类型
- 维护成本:考虑索引的维护成本
- 测试验证:测试不同索引策略的性能
Q10: 如何使用自动索引功能?
A10: 使用自动索引功能的方法:
- 启用自动索引:在Oracle 19c及以上版本启用自动索引
- 监控自动索引:监控自动索引的创建和使用情况
- 验证效果:验证自动索引对性能的提升
- 调整配置:根据需要调整自动索引的配置
- 结合手动:将自动索引与手动索引管理结合使用
Q11: 如何评估索引的有效性?
A11: 评估索引有效性的方法:
- 执行计划:查看执行计划,确认索引是否被使用
- 性能测试:测试索引对查询性能的提升
- 使用监控:监控索引的使用情况
- 维护成本:评估索引的维护成本
- 空间使用:评估索引的存储空间使用
Q12: 如何处理大数据量表的索引?
A12: 处理大数据量表索引的方法:
- 分区索引:使用分区索引分散索引负载
- 并行操作:使用并行操作创建和重建索引
- 在线操作:使用在线操作减少对业务的影响
- 索引设计:精心设计索引,避免过多索引
- 定期维护:建立定期的索引维护计划
- 监控性能:监控索引性能,及时调整
Q13: 如何优化位图索引的性能?
A13: 优化位图索引性能的方法:
- 低基数列:仅在低基数列上使用位图索引
- 避免更新:避免在频繁更新的列上使用位图索引
- 数据仓库:在数据仓库环境中使用位图索引
- 复合位图索引:结合使用多个低基数列创建复合位图索引
- 与B树结合:与B树索引结合使用,优化不同场景
Q14: 如何处理索引统计信息过时的问题?
A14: 处理索引统计信息过时的方法:
- 自动收集:启用自动统计信息收集
- 手动收集:定期手动收集统计信息
- 增量收集:对于分区表,使用增量统计信息收集
- 并行收集:使用并行操作收集统计信息
- 监控统计信息:监控统计信息的状态,及时更新
Q15: 如何使用索引提示优化查询性能?
A15: 使用索引提示优化查询性能的方法:
- 识别问题:识别优化器选择错误的情况
- 选择提示:选择合适的索引提示类型
- 测试验证:测试索引提示对性能的影响
- 谨慎使用:仅在必要时使用索引提示
- 文档记录:记录使用索引提示的原因和效果
索引管理最佳实践
设计阶段
- 需求分析:基于业务需求和查询模式设计索引
- 选择性评估:评估列的选择性,选择合适的索引类型
- 复合索引:合理设计复合索引,考虑列顺序
- 覆盖查询:设计覆盖常用查询的索引
- 测试验证:在测试环境验证索引效果
部署阶段
- 渐进部署:在生产环境逐步部署索引
- 在线操作:使用在线操作减少对业务的影响
- 并行操作:对于大型索引,使用并行操作
- 监控性能:部署后监控查询性能
- 回滚计划:准备索引部署的回滚计划
维护阶段
- 定期重建:定期重建碎片化的索引
- 统计信息:定期更新索引统计信息
- 使用监控:监控索引使用情况
- 碎片管理:定期整理索引碎片
- 优化调整:根据使用情况调整索引
优化阶段
- 性能分析:定期分析索引性能
- 问题识别:识别索引相关的性能问题
- 优化调整:调整索引结构和配置
- 新技术应用:应用新的索引技术和特性
- 持续改进:持续优化索引设计和管理
淘汰阶段
- 使用评估:评估索引的使用情况
- 性能影响:评估删除索引对查询性能的影响
- 冗余清理:清理冗余和未使用的索引
- 备份:删除索引前确保有备份
- 文档更新:更新索引相关的文档
