Oracle 索引教程

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 自动为主键创建唯一索引)。

  • 频繁出现在 WHEREJOINORDER BYGROUP 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'; -- 不生效(未用函数)

五、索引失效的常见原因

  1. 使用函数/运算WHERE UPPER(ename) = 'SCOTT'WHERE sal + 100 > 2000(除非有函数索引)。

  2. 隐式类型转换WHERE empno = '7788'(empno 是 NUMBER 类型,字符串会触发转换)。

  3. 否定操作符!=<>NOT INNOT LIKE(可能导致全表扫描)。

  4. IS NULL / IS NOT NULL:B-tree 索引不存储 NULL 值(除非是复合索引且其他列非空)。

  5. LIKE 以通配符开头LIKE '%SCOTT'(无法使用索引,LIKE 'SCOTT%'可以)。

  6. 复合索引违反最左前缀 :索引 (a,b,c),查询 WHERE b=1 AND c=2(缺少 a)。


六、验证索引是否生效(执行计划)

使用 EXPLAIN PLANAUTOTRACE查看查询是否走索引:

复制代码
-- 方法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;

核心原则:索引是"空间换时间"的策略,需根据查询需求和数据特点合理设计,定期维护(重建、清理无效索引)。

需要我用一个具体的业务场景 (如员工查询)演示如何设计复合索引 ,并分析其执行计划吗?