索引优化是 SQL 优化中最有效、性价比最高的技术手段,正确的索引设计能将 SQL 执行速度提升 10-1000 倍。它的核心本质是用空间换时间,通过构建有序的数据结构,将全表扫描的 O (n) 时间复杂度降低为索引扫描的 O (log n)。
1、索引基础
1.1、什么是索引
索引是一种与表关联的可选数据库对象,它存储了表中一列或多列的值以及对应的行物理地址(ROWID),能够快速定位到满足条件的数据行,而无需扫描整个表。
简单理解:索引就像书的目录,通过目录可以快速找到章节内容,而不需要逐页翻阅整本书。
1.2、索引的核心作用
- 加速查询:大幅减少数据访问量,提高 SELECT、UPDATE、DELETE 语句的执行速度
- 加速排序和分组:如果 ORDER BY/GROUP BY 的列有索引,Oracle 可以直接使用索引的有序性,避免排序操作
- 保证数据唯一性:唯一索引可以确保列值不重复
- 加速表连接:连接列上的索引可以大幅提升多表连接性能
1.3、索引的开销
索引会带来以下开销:
- 存储开销:索引需要占用额外的磁盘空间(通常为表大小的 10%-30%)
- 写入开销:INSERT/UPDATE/DELETE 操作会同时更新所有相关索引,降低写入性能
- 维护开销:索引会产生碎片,需要定期重建或合并
索引是一把双刃剑,只在必要的列上创建索引,避免过度索引。
2、索引类型
2.1、B 树索引(默认索引)
- 特点:最通用的索引类型,支持等值查询、范围查询、排序和分组
- 适用场景:高基数列(不同值多的列,如员工号、订单号)、OLTP 系统
- 不适用场景:低基数列(如性别、状态)、频繁更新的列
- 创建语法:CREATE INDEX idx_emp_ename ON emp(ename);
2.2、唯一索引
- 特点:确保索引列的值唯一,主键约束会自动创建唯一索引
- 适用场景:主键、唯一键列
- 性能优势:查询时找到第一个匹配项就停止,比普通索引更快
- 创建语法:CREATE UNIQUE INDEX idx_emp_empno ON emp(empno);
2.3、复合索引(组合索引)
- 特点:基于多个列创建的索引,是生产环境中最常用的索引类型
- 核心原则:等值查询列在前,范围查询列在后,过滤性好的列在前
- 前缀性原则:只有查询条件包含索引的前导列时,才能使用该索引
- 创建语法:CREATE INDEX idx_emp_deptno_sal ON emp(deptno, sal);
2.4、函数索引
- 特点:基于列的函数或表达式创建的索引,解决 "索引列上使用函数导致索引失效" 的问题
- 适用场景:查询中经常对列使用函数或表达式
- 创建语法 :
- 解决WHERE UPPER(ename)='SMITH'索引失效问题
- CREATE INDEX idx_emp_upper_ename ON emp(UPPER(ename));
2.5、位图索引
- 特点:使用位图存储索引信息,每个位代表一行数据是否包含该键值
- 适用场景:低基数列(如性别、状态)、数据仓库、只读表、批量更新
- 不适用场景:高并发 OLTP 系统、频繁更新的列
- 创建语法:CREATE BITMAP INDEX idx_emp_gender ON emp(gender);
2.6、反向键索引
- 特点:将索引键值的字节顺序反转,解决 "索引热点块" 问题
- 适用场景:序列生成的主键列(如订单号、用户 ID),这些列的值是连续递增的,会导致索引的右侧叶子节点成为热点
- 创建语法:CREATE INDEX idx_orders_order_id ON orders(order_id) REVERSE;
2.7、分区索引
- 特点:与分区表配合使用,每个分区对应一个独立的索引分区
- 类型 :
- 本地分区索引:索引分区与表分区一一对应(推荐)
- 全局分区索引:索引分区与表分区不对应
- 适用场景:大于 10GB 的分区表
- 本地创建语法:CREATE INDEX idx_trans_trans_date ON transactions(trans_date) LOCAL;
2.8、索引组织表(IOT)
- 特点:表的数据按索引的顺序存储,表本身就是索引,没有单独的表段
- 适用场景:小表、查询主要通过主键访问的表
- 优势:避免回表操作,查询速度极快
- 创建语法:
sql
CREATE TABLE emp_iot (
empno NUMBER PRIMARY KEY,
ename VARCHAR2(20),
sal NUMBER
) ORGANIZATION INDEX;
3、索引扫描类型
执行计划中的索引扫描类型直接决定了 SQL 的性能,必须熟练掌握每种扫描的触发条件和特点。
| 扫描类型 | 英文名称 | 触发条件 | 性能 | 说明 |
|---|---|---|---|---|
| 唯一索引扫描 | INDEX UNIQUE SCAN | 唯一索引的等值查询 | 最优 | 最多返回一行数据,找到后立即停止 |
| 索引范围扫描 | INDEX RANGE SCAN | 索引的范围查询(>、<、BETWEEN、LIKE 'xxx%') | 优 | 扫描索引的一部分叶子节点 |
| 索引快速全扫描 | INDEX FAST FULL SCAN | 查询所有索引列,不需要排序 | 中 | 多块读扫描整个索引,比全表扫描快 |
| 索引全扫描 | INDEX FULL SCAN | 需要排序的结果集,且排序列是索引列 | 中 | 单块读扫描整个索引,结果有序 |
| 索引跳跃扫描 | INDEX SKIP SCAN | 复合索引的前导列没有出现在查询条件中,但后续列有过滤条件 | 差 | 11g + 特性,性能不如包含前导列的查询 |
| 全表扫描 | TABLE ACCESS FULL | 没有合适的索引,或全表扫描比索引扫描更快 | 最差 | 扫描整个表的所有数据块 |
关键说明:
- 索引快速全扫描和索引全扫描的区别:快速全扫描是多块读,结果无序;全扫描是单块读,结果有序
- 索引跳跃扫描是 Oracle 的优化手段,但性能不稳定,最好避免依赖它
- 小表(<1000 行)的全表扫描可能比索引扫描更快,因为索引扫描需要两次 IO(索引 + 回表)
实例:某公司 ERP 索引优化
**S(Situation - 情境):**某数据库运行2年后,频繁DML操作导致多个核心索引碎片率超过40%,索引高度从3层增长到5层,查询性能持续下降。
**T(Task - 任务):**制定索引维护计划,消除碎片、降低索引高度,恢复查询性能。
A(Action - 行动):
1、分析所有核心索引的碎片率
2、碎片率>30%的索引使用ALTER INDEX ... REBUILD ONLINE重建
3、碎片率10%-30%的使用COALESCE合并
4、在业务低峰期分批执行
**R(Result - 结果):**平均索引高度从5层降至3层,索引范围扫描性能提升40%-60%,并在线重建未影响业务正常运行,建立每季度索引维护计划。