
索引是 Oracle 数据库中提升查询性能的核心工具,通过 “空间换时间” 的策略,减少数据检索时的 I/O 操作。本文将系统讲解 Oracle 索引的原理、分类、使用限制及维护技巧,帮助数据库管理员和开发人员合理设计索引,平衡查询效率与维护成本。
索引是一种可选的数据库对象,主要作用是加速查询操作。其核心原理是:将用户关注的列值(索引键)与对应行的物理位置标识(ROWID)存储在一起,形成有序的数据结构。当执行查询时,Oracle 可通过索引快速定位到目标数据所在的物理位置,避免全表扫描(Full Table Scan)带来的大量 I/O 消耗。
- ROWID 的作用:ROWID 是 Oracle 为每行数据分配的唯一标识符,包含数据文件号、块号、行号等信息,直接指向数据在磁盘上的物理位置。通过索引中的 ROWID,Oracle 能快速定位到目标行,甚至判断多行是否存储在同一个数据块中(减少块读取次数)。
- 空间换时间:索引会占用额外的存储空间(通常为表大小的 10%-30%),但能显著减少查询的 I/O 次数(全表扫描可能需要读取数千个块,而索引扫描可能只需几十个),尤其对大表查询效果明显。
Oracle 通过数据字典视图记录索引的元信息,常用视图包括user_indexes(索引基本信息)和user_ind_columns(索引包含的列信息)。
创建索引后,可通过user_indexes查询某张表的所有索引:
create index emp_id2 on emp (sal); select table_name, index_name, uniqueness from user_indexes where table_name = 'EMP';
通过user_ind_columns可查看索引包含的列及列顺序(复合索引的列顺序影响查询效率):
column index_name format a15 column column_name format a10 column table_name format a10 select table_name, index_name, column_name, column_position from user_ind_columns where table_name = 'EMP' order by index_name, column_position;
索引虽能提升查询性能,但并非万能。以下场景中,索引可能失效或效率低于全表扫描,需避免盲目使用:
表数据量极小(如少于 100 行):全表扫描的 I/O 成本可能低于索引扫描(索引本身也需 I/O)。
查询返回表中大部分数据(如超过 30%):此时全表扫描可能比索引扫描更高效(索引扫描需先读索引,再根据 ROWID 读数据,反而增加 I/O)。
索引列频繁被修改(INSERT/UPDATE/DELETE):索引会随数据修改同步更新,频繁修改会导致索引维护成本激增(如日志生成、碎片增加)。
查询中索引列使用函数或表达式:如where upper(ename) = 'SCOTT',若ename上有索引,该查询无法使用索引(需创建基于函数的索引解决)。
使用特定运算符:
- 不等于(!=、<>)、NOT IN:可能导致索引失效,改用NOT EXISTS更高效;
- IS NULL/IS NOT NULL:若索引列允许为空,NULL 值不被索引存储,查询可能走全表扫描;
- 模糊查询以%开头(如where ename like '%SMITH'):无法使用索引(以%结尾可使用索引)。
Oracle 提供多种索引类型,适用于不同业务场景,选择正确的索引类型是性能优化的关键。
B 树索引是 Oracle 最常用的索引类型,得名于其类似 “平衡树”(Balanced Tree)的结构,由根节点、分支节点和叶节点组成,叶节点存储索引键和对应的 ROWID,且叶节点按索引键有序排列。
- 核心优势:通过层级结构快速定位索引键,减少 I/O 操作(如百万级数据,通常只需 3-4 次 I/O 即可定位)。
- 适用场景:高基数列(列值重复率低,如身份证号、手机号)、频繁范围查询(如between、>、<)。
- 维护统计信息:索引的使用依赖于 Oracle 优化器的统计信息,需定期更新:
exec dbms_stats.gather_table_stats( ownname => 'SYS', tabname => 'T1', method_opt => 'for all indexed columns size 2', cascade => TRUE );
索引组织表(Index-Organized Table)与普通表的核心区别是:数据直接按索引结构存储,没有独立的表存储空间。普通表中数据无序存储,索引是独立的结构;而 IOT 中数据本身就是索引的一部分,按索引键有序排列。
- 适用场景:
- 频繁通过主键查询的表(如字典表、配置表);
- 范围查询频繁的表(如按时间排序的日志表);
- 需避免表与索引分离存储的场景(减少 I/O 次数)。
- 创建示例:
create table emp_iot ( empno number(4) primary key, ename varchar2(10), sal number(7,2) ) organization index;
唯一索引是 B 树索引的特殊形式,强制索引列的值唯一(允许 NULL 值,但 NULL 值可重复)。主键约束会自动创建唯一索引,但唯一索引不等于主键(主键不允许 NULL,唯一索引允许)。
- 适用场景:需保证列值唯一的场景(如用户 ID、订单号)。
- 创建示例:
create unique index idx_emp_empno on emp(empno); create table test_uid ( name varchar(10), constraint test_uid_pk primary key(name) using index (create unique index uid_test_uid on test_uid(name) tablespace users) );
反向键索引将索引键值反转后存储(如 “12345” 存储为 “54321”),打破原有序列的连续性,避免多个会话同时插入相邻键值时的叶节点争用(如序列生成的主键)。
- 适用场景:
- 高并发插入场景(如订单表,主键为序列自增);
- 索引键值连续增长,导致叶节点集中在某一区域(热块争用)。
- 创建示例:
create index idx_orders_id_rev on orders(id) reverse;
降序索引按索引键的降序存储,适用于频繁按某列降序查询的场景(如 “查询最新 10 条记录”),可避免查询时的排序操作(ORDER BY ... DESC)。
- 创建示例:
create index idx_emp_sal_desc on emp(sal desc); select ename, sal from emp order by sal desc fetch first 10 rows only;
位图索引为每个索引键值创建一个位图(bitmask),其中每一位对应表中的一行,标识该行是否包含该键值。适用于低基数列(列值重复率高,如性别、状态、部门类型,通常不超过 100 个不同值)。
- 优势:存储空间小(位图压缩存储),适合多列组合查询(如where gender='M' and deptno=10)。
- 限制:不适合高并发 DML 场景(更新时可能锁定多个位图,导致性能下降)。
- 创建示例:
create bitmap index idx_emp_gender on emp(gender);
当查询中对索引列使用函数(如upper(ename)、trunc(hiredate))时,普通索引无法使用。基于函数的索引预先计算函数结果并存储,解决此类问题。
- 适用场景:查询中频繁对列使用固定函数(如大小写转换、日期截断)。
- 创建示例:
create index idx_emp_upper_ename on emp(upper(ename)); select ename from emp where upper(ename) = 'SCOTT';
索引使用一段时间后,可能因频繁 DML 操作产生碎片(叶节点空闲空间过多),导致查询性能下降。需定期维护索引,包括重建、合并及监控无用索引。
当索引碎片严重(如叶节点利用率低于 50%)时,需重建索引。重建会创建新的索引结构,释放碎片空间,提升查询效率。
alter index cust_idx1
rebuild
parallel 8 tablespace cust_tblspc1 online;
若索引碎片较少,可通过合并(coalesce)操作将相邻叶节点的空闲空间合并,无需重建整个索引,开销更小。
alter index pk_emp coalesce;
部分索引可能创建后从未被使用,浪费存储空间和维护成本。可通过索引监控功能识别无用索引。
alter index EMP_ENAME_SAL_IDX monitoring usage; select index_name, table_name, monitoring, used from v$object_usage; select index_name, start_monitoring, end_monitoring from v$object_usage where index_name = 'EMP_ENAME_SAL_IDX'; alter index EMP_ENAME_SAL_IDX nomonitoring usage; drop index dept_dname_idx;
- 按需创建:根据查询场景选择合适的索引类型(如高基数列用 B 树索引,低基数列用位图索引),避免 “索引越多越好”。
- 平衡读写:索引提升查询性能,但会降低 INSERT/UPDATE/DELETE 效率,写入频繁的表需控制索引数量。
- 定期维护:通过重建或合并处理碎片,通过监控删除无用索引,保持索引高效。
- 结合执行计划:创建索引后,需通过explain plan分析查询执行计划,确认索引被有效使用。
合理的索引策略是 Oracle 数据库性能优化的基础,需结合业务查询特征、数据量和读写比例综合设计,才能最大化发挥索引的 “空间换时间” 价值。