Oracle 索引基础教程
一、什么是索引?
**索引(Index)** 是数据库对象,类似于书籍的目录,用于加速数据查询。它通过维护表中一列或多列值的排序结构,让数据库无需全表扫描即可快速定位数据。
核心作用:提高查询效率,但会降低 DML(INSERT/UPDATE/DELETE)性能(因为需要维护索引结构)。
二、索引的核心类型
1. B-tree 索引(默认,最常用)
-
结构:平衡树(Balanced Tree),类似二叉树但层级更多,适合高基数列(列值重复少)。
-
适用场景 :等值查询(
=)、范围查询(>、<、BETWEEN)、排序(ORDER BY)。 -
示例:主键索引、唯一索引。
2. 位图索引(Bitmap Index)
-
结构:用二进制位(0/1)表示列值的存在性,适合低基数列(列值重复多,如性别、状态)。
-
适用场景:数据仓库、OLAP 系统,不适合频繁更新的 OLTP 系统。
-
限制 :不支持
NULL值,更新时可能锁大量数据。
3. 函数索引(Function-Based Index)
-
结构:基于表达式或函数的结果创建索引。
-
适用场景 :查询条件中包含函数(如
UPPER(name)、TO_CHAR(date_col))。 -
示例 :
CREATE INDEX idx_emp_name_upper ON emp(UPPER(ename));
4. 复合索引(组合索引)
-
结构:基于多列创建的索引,列顺序影响性能。
-
适用场景 :多列联合查询(如
WHERE deptno=10 AND job='CLERK')。 -
最左前缀原则 :查询条件必须包含索引的最左列才能生效(如索引
(a,b,c),查询a=1 AND b=2有效,b=2 AND c=3无效)。
5. 唯一索引(Unique Index)
-
作用 :确保索引列的值唯一(允许
NULL,但NULL可重复)。 -
创建方式 :
CREATE UNIQUE INDEX ...或主键/唯一约束自动创建。
三、索引的基本操作
1. 创建索引
-- 普通 B-tree 索引
CREATE INDEX idx_emp_deptno ON emp(deptno);
-- 唯一索引
CREATE UNIQUE INDEX idx_emp_empno ON emp(empno);
-- 复合索引(deptno 在前,job 在后)
CREATE INDEX idx_emp_dept_job ON emp(deptno, job);
-- 位图索引
CREATE BITMAP INDEX idx_emp_gender ON emp(gender);
-- 函数索引
CREATE INDEX idx_emp_hire_year ON emp(EXTRACT(YEAR FROM hiredate));
2. 查看索引
-- 查看用户所有索引
SELECT index_name, table_name, uniqueness, index_type
FROM user_indexes;
-- 查看索引列
SELECT index_name, column_name, column_position
FROM user_ind_columns
WHERE table_name = 'EMP'
ORDER BY index_name, column_position;
-- 查看索引使用情况(需开启监控)
ALTER INDEX idx_emp_deptno MONITORING USAGE;
SELECT * FROM v$object_usage WHERE index_name = 'IDX_EMP_DEPTNO';
3. 修改索引
-- 重建索引(解决碎片问题,提升性能)
ALTER INDEX idx_emp_deptno REBUILD;
-- 重命名索引
ALTER INDEX idx_emp_deptno RENAME TO idx_emp_dept;
-- 禁用/启用索引
ALTER INDEX idx_emp_deptno DISABLE; -- 仅位图索引支持
ALTER INDEX idx_emp_deptno ENABLE;
4. 删除索引
DROP INDEX idx_emp_deptno;
四、索引设计最佳实践
1. 何时创建索引
-
主键/外键列(Oracle 自动为主键创建唯一索引)。
-
频繁出现在
WHERE、JOIN、ORDER BY、GROUP BY中的列。 -
高选择性列(如身份证号、手机号)。
-
低选择性列(如性别、状态,除非是位图索引)。
-
频繁更新的列(每次更新需维护索引)。
-
小表(全表扫描比索引扫描更快)。
-
2. 复合索引的列顺序
-
将 选择性高的列放在前面(减少扫描范围)。
-
将 等值查询列放在前面 ,范围查询列放在后面(如
WHERE a=1 AND b>10,索引(a,b)优于(b,a))。
3. 避免过度索引
-
每个表索引数建议不超过 5-7 个(DML 性能会下降)。
-
定期清理未使用的索引(
v$object_usage监控)。
4. 函数索引的陷阱
-
查询条件必须与函数索引的表达式完全一致才能生效。
-- 函数索引:UPPER(ename) SELECT * FROM emp WHERE UPPER(ename) = 'SCOTT'; -- 生效 SELECT * FROM emp WHERE ename = 'SCOTT'; -- 不生效(未用函数)
五、索引失效的常见原因
-
使用函数/运算 :
WHERE UPPER(ename) = 'SCOTT'或WHERE sal + 100 > 2000(除非有函数索引)。 -
隐式类型转换 :
WHERE empno = '7788'(empno 是 NUMBER 类型,字符串会触发转换)。 -
否定操作符 :
!=、<>、NOT IN、NOT LIKE(可能导致全表扫描)。 -
IS NULL / IS NOT NULL:B-tree 索引不存储 NULL 值(除非是复合索引且其他列非空)。
-
LIKE 以通配符开头 :
LIKE '%SCOTT'(无法使用索引,LIKE 'SCOTT%'可以)。 -
复合索引违反最左前缀 :索引
(a,b,c),查询WHERE b=1 AND c=2(缺少 a)。
六、验证索引是否生效(执行计划)
使用 EXPLAIN PLAN或 AUTOTRACE查看查询是否走索引:
-- 方法1:EXPLAIN PLAN
EXPLAIN PLAN FOR
SELECT * FROM emp WHERE deptno = 10;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 方法2:AUTOTRACE(SQL*Plus 中)
SET AUTOTRACE ON;
SELECT * FROM emp WHERE deptno = 10;
关键指标:
-
INDEX RANGE SCAN/INDEX UNIQUE SCAN:索引生效。 -
TABLE ACCESS FULL:全表扫描(索引未生效)。
七、总结
| 操作 | 命令示例 |
|---|---|
| 创建普通索引 | CREATE INDEX idx_name ON table(col); |
| 创建唯一索引 | CREATE UNIQUE INDEX idx_name ON table(col); |
| 创建复合索引 | CREATE INDEX idx_name ON table(col1, col2); |
| 查看索引 | SELECT * FROM user_indexes WHERE table_name='EMP'; |
| 重建索引 | ALTER INDEX idx_name REBUILD; |
| 删除索引 | DROP INDEX idx_name; |
核心原则:索引是"空间换时间"的策略,需根据查询需求和数据特点合理设计,定期维护(重建、清理无效索引)。
需要我用一个具体的业务场景 (如员工查询)演示如何设计复合索引 ,并分析其执行计划吗?