索引原理与应用

最后更新: 2026-04-15 作者: Oracle Team
页面目录

第五章:索引原理与应用

文档信息
  • 适用版本:Oracle 12c / 18c / 19c / 21c
  • 阅读时间:约 50 分钟
  • 前置知识:第四章数据库与表操作

5.1 索引概述

5.1.1 索引工作原理

┌─────────────────────────────────────────────────────────────────┐
│                      B-Tree 索引结构                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│                         [ROOT]                                   │
│                        /      \                                  │
│                    [BRANCH]   [BRANCH]                           │
│                    /    \      /    \                            │
│                 [LEAF] [LEAF] [LEAF] [LEAF]                     │
│                    │      │      │      │                        │
│                 ────────────────────────────────────             │
│                    │      │      │      │                        │
│                 ┌─────┬─────┬─────┬─────┬─────┐                │
│                 │ 10  │ 20  │ 30  │ 40  │ 50  │ ← 键值         │
│                 │ ROW │ ROW │ ROW │ ROW │ ROW │ ← 行指针       │
│                 └─────┴─────┴─────┴─────┴─────┘                │
│                                                                 │
│  索引查找流程:                                                  │
│  1. 从 ROOT 开始查找                                            │
│  2. 根据 BRANCH 判断目标范围                                    │
│  3. 定位到 LEAF 节点                                            │
│  4. 通过 ROWID 直接访问数据行                                    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.1.2 索引 vs 全表扫描

特性 索引扫描 全表扫描
适用场景 高选择性列 低选择性列
I/O 次数 少量 I/O 大量 I/O
结果顺序 按索引顺序 无序
排序 自动有序 可能需额外排序
回表 可能需要 直接访问

5.2 B-Tree 索引

5.2.1 B-Tree 结构详解

-- 创建 B-Tree 索引
CREATE INDEX idx_emp_name ON employees(last_name, first_name);

-- 查看索引结构
SELECT 
    index_name,
    index_type,
    table_name,
    uniqueness,
    compression,
    leaf_blocks,
    distinct_keys,
    clustering_factor
FROM user_indexes
WHERE index_name = 'IDX_EMP_NAME';

-- 查看索引列
SELECT 
    index_name,
    column_name,
    column_position,
    descend
FROM user_ind_columns
WHERE index_name = 'IDX_EMP_NAME'
ORDER BY column_position;

5.2.2 复合索引设计原则

复合索引列顺序原则:

1. 等值查询列放在前面
   ✅ CREATE INDEX idx ON table(a, b) WHERE a=? AND b=?
   ❌ CREATE INDEX idx ON table(b, a)

2. 高选择性列放前面
   ✅ 性别(低) + 身份证号(高) → 列顺序: 身份证号, 性别

3. 考虑覆盖索引
   ✅ SELECT name, age FROM table WHERE name='张三';
   ✅ CREATE INDEX idx ON table(name) INCLUDE (age); -- 覆盖索引

复合索引使用规则:
┌──────────────────────────────────────────────────┐
│ 查询条件              │ 索引使用情况              │
├──────────────────────────────────────────────────┤
│ WHERE a=? AND b=?    │ ✅ 完全使用 (a,b)         │
│ WHERE a=?            │ ✅ 使用前导列 (a)          │
│ WHERE b=?            │ ❌ 无法使用 (b不是前导列)   │
│ WHERE b=? AND a=?    │ ✅ 优化器重排使用 (a,b)    │
└──────────────────────────────────────────────────┘

5.3 Bitmap 索引

5.3.1 Bitmap 原理

┌─────────────────────────────────────────────────────────────────┐
│                      Bitmap 索引原理                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  示例:employees 表                                             │
│  ─────────────────────────────                                  │
│  EMP_ID  GENDER  STATUS                                        │
│  ──────  ──────  ──────                                        │
│    1001    M      ACTIVE                                        │
│    1002    F      INACTIVE                                      │
│    1003    M      ACTIVE                                         │
│    1004    F      ACTIVE                                         │
│                                                                 │
│  Bitmap 索引结构:                                               │
│  ─────────────────                                              │
│  GENDER='M'  →  1010  (1001,1003 为 M)                         │
│  GENDER='F'  →  0101  (1002,1004 为 F)                         │
│                                                                 │
│  STATUS='ACTIVE'  →  1011  (1001,1003,1004 活跃)               │
│                                                                 │
│  Bitmap 位运算:                                                 │
│  ────────────                                                   │
│  M + ACTIVE  →  1010 AND 1011 = 1010  →  1001,1003             │
│  F + ACTIVE  →  0101 AND 1011 = 0001  →  1004                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.3.2 Bitmap 索引应用场景

-- 创建 Bitmap 索引
CREATE BITMAP INDEX idx_emp_gender ON employees(gender);
CREATE BITMAP INDEX idx_emp_status ON employees(status);
CREATE BITMAP INDEX idx_emp_dept ON employees(department_id);

-- 适合场景:
-- 1. 低基数列(枚举值少)
-- 2. 多个 Bitmap 索引组合查询
-- 3. 数据仓库 / OLAP 系统
-- 4. 只读或极少更新表

-- 不适合场景:
-- 1. 高基数列(唯一值多)
-- 2. OLTP 频繁更新
-- 3. 并发写入多的表

5.3.3 Bitmap Join 索引

-- 预计算连接结果
CREATE BITMAP INDEX idx_sales_cust_name
ON sales(customers.customer_name)
FROM sales, customers
WHERE sales.customer_id = customers.customer_id;

5.4 函数索引与表达式索引

5.4.1 函数索引

-- 大小写不敏感查询
CREATE INDEX idx_emp_upper_email ON employees(UPPER(email));

-- 查询可使用索引
SELECT * FROM employees WHERE UPPER(email) = 'JOHN.DOE@EXAMPLE.COM';

-- 计算列索引
CREATE INDEX idx_emp_annual_sal ON employees(salary * 12);

-- 日期函数索引
CREATE INDEX idx_emp_hire_year ON employees(EXTRACT(YEAR FROM hire_date));

-- 自定义函数索引
CREATE INDEX idx_emp_name_reverse ON employees(REVERSE(last_name));

-- FBI 使用示例
SELECT * 
FROM employees 
WHERE LOWER(last_name) = 'smith' 
AND hire_date >= TO_DATE('2020-01-01', 'YYYY-MM-DD');

5.5 索引监控与诊断

5.5.1 监控索引使用

-- 开启索引监控
ALTER INDEX idx_emp_name MONITORING USAGE;

-- 执行查询
SELECT * FROM employees WHERE last_name = 'Smith';

-- 检查使用情况
SELECT 
    index_name,
    table_name,
    monitoring,
    used,
    start_monitoring,
    end_monitoring
FROM v$object_usage
WHERE index_name = 'IDX_EMP_NAME';

-- 关闭监控
ALTER INDEX idx_emp_name NOMONITORING USAGE;

5.5.2 查看索引效率

-- 查看索引统计信息
SELECT 
    index_name,
    blevel,                    -- B-Tree 层数
    leaf_blocks,               -- 叶子块数
    distinct_keys,             -- 唯一键数
    avg_leaf_blocks_per_key,   -- 每键叶子块数
    avg_data_blocks_per_key,  -- 每键数据块数
    clustering_factor,         -- 聚簇因子
    num_rows
FROM user_indexes
WHERE table_name = 'EMPLOYEES';

-- Clustering Factor 说明:
-- 接近表块数:数据有序,索引效率高
-- 接近行数:数据无序,需大量 I/O

5.5.3 识别无效/不可用索引

-- 查看索引状态
SELECT 
    owner,
    index_name,
    index_type,
    status,
    num_rows,
    last_analyzed
FROM dba_indexes
WHERE status NOT IN ('VALID', 'N/A')
OR last_analyzed IS NULL;

-- 状态说明:
-- VALID: 正常
-- UNUSABLE: 不可用,需要重建
-- N/A: 分区索引未使用特定分区

-- 重建不可用索引
ALTER INDEX idx_name REBUILD;
ALTER INDEX idx_name REBUILD ONLINE;

-- 检查分区索引状态
SELECT 
    index_name,
    partition_name,
    status
FROM dba_ind_partitions
WHERE status != 'USABLE';

5.6 索引优化策略

5.6.1 何时创建索引

┌─────────────────────────────────────────────────────────────────┐
│                      索引创建决策树                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  列的唯一值比例 > 95% ?                                         │
│      ↓ 是                                                       │
│  ✅ 创建 B-Tree 索引                                            │
│                                                                 │
│  列的唯一值比例 5-95% + 多列组合查询 ?                           │
│      ↓ 是                                                       │
│  ✅ 创建复合 B-Tree 索引                                        │
│                                                                 │
│  列的唯一值比例 < 5% + 只读表 ?                                 │
│      ↓ 是                                                       │
│  ✅ 创建 Bitmap 索引                                            │
│                                                                 │
│  WHERE/ORDER BY 使用函数或表达式 ?                               │
│      ↓ 是                                                       │
│  ✅ 创建函数索引                                                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.6.2 索引优化技巧

-- 1. 覆盖索引(避免回表)
-- 查询涉及列都在索引中
CREATE INDEX idx_emp_covering ON employees(department_id, salary) 
INCLUDE (first_name, last_name);  -- 覆盖列

-- 2. 分区索引
-- 本地索引:与分区一致,便于分区操作
CREATE INDEX idx_sales_date ON sales(sale_date) LOCAL;

-- 全局索引:跨分区
CREATE INDEX idx_orders_date ON orders(order_date) GLOBAL;

-- 3. 压缩索引
CREATE INDEX idx_emp_name ON employees(last_name) COMPRESS 1;

-- 4. 不可见索引
CREATE INDEX idx_emp_new ON employees(manager_id) INVISIBLE;
ALTER INDEX idx_emp_new VISIBLE;  -- 启用
ALTER INDEX idx_emp_new INVISIBLE; -- 禁用

-- 5. 虚拟列索引
ALTER TABLE employees ADD (full_name AS (first_name || ' ' || last_name));
CREATE INDEX idx_emp_fullname ON employees(full_name);

5.7 执行计划分析

5.7.1 读取执行计划

-- 方法1: EXPLAIN PLAN
EXPLAIN PLAN FOR
SELECT * FROM employees WHERE last_name = 'Smith';
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

-- 方法2: AUTOTRACE
SET AUTOTRACE ON;
SELECT * FROM employees WHERE last_name = 'Smith';
SET AUTOTRACE OFF;

-- 方法3: DBMS_XPLAN
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(null, null, 'ALLSTATS LAST'));

5.7.2 索引扫描类型

操作 说明 提示
INDEX UNIQUE SCAN 唯一索引扫描 返回 0-1 行
INDEX RANGE SCAN 范围扫描 返回多行
INDEX FULL SCAN 索引全扫描 有序返回
INDEX FAST FULL SCAN 索引快速全扫描 无序,多块读
INDEX SKIP SCAN 跳跃扫描 复合索引前导列未使用
INDEX JOIN SCAN 索引连接 多索引合并

5.7.3 优化案例

-- 问题查询
SELECT employee_id, first_name, salary
FROM employees
WHERE TO_CHAR(hire_date, 'YYYY') = '2024';

-- 优化:使用函数索引
CREATE INDEX idx_hire_year ON employees(EXTRACT(YEAR FROM hire_date));

-- 优化后查询
SELECT employee_id, first_name, salary
FROM employees
WHERE EXTRACT(YEAR FROM hire_date) = 2024;

-- 问题查询
SELECT * FROM orders WHERE status != 'CANCELLED';

-- 优化建议:如果是低选择性的排除查询,考虑分区
CREATE BITMAP INDEX idx_order_status ON orders(status);

5.8 本章小结

✅ B-Tree 索引:默认索引,适合高选择性列
✅ Bitmap 索引:适合低基数列,多列组合查询
✅ 函数索引:优化函数查询
✅ 复合索引:遵循最左前缀原则设计
✅ 索引监控:定期检查索引使用情况
✅ 执行计划:分析索引效率,指导优化

📖 下章预告:视图与存储过程

下一步:学习 第六章:视图与存储过程