5 Indexes and Index-Organized Tables
Overview of B-Tree Indexes
Index Scans
索引扫描
在索引扫描中,数据库通过遍历索引,使用语句指定的索引列值来检索行。如果数据库为某个值扫描索引,它将在 n 个 I/O 操作中找到该值,其中 n 是 B 树索引的高度。这是 Oracle 数据库索引的基本原理。
如果 SQL 语句仅访问索引列,那么数据库将直接从索引中读取值,而不是从表中读取。如果语句除了访问索引列之外还访问非索引列,那么数据库将使用行标识来查找表中的行。通常,数据库通过交替读取索引块和表块的方式来检索表数据。
- 完全索引扫描
在完全索引扫描中,数据库按顺序读取整个索引。如果 SQL 语句中的谓词(WHERE 子句)引用了一个索引列,或者在某些情况下不指定任何谓词,那么就可能使用完全索引扫描。完全扫描可以消除排序,因为数据已经按照索引键排序了。 - 快速完全索引扫描
快速完全索引扫描是一种直接访问索引本身的数据而无需访问表的完全索引扫描,数据库并不以特定的顺序读取索引块。 - 索引范围扫描
索引范围扫描是索引的有序扫描,其中在条件中指定了索引的一个或多个前导列,并且索引键可能对应 0、1 或多个值。 - 索引唯一扫描
与索引范围扫描相反,索引唯一扫描必须有0个或1个行标识与索引键相关联。 - 索引跳跃扫描
索引跳跃扫描使用复合索引的逻辑子索引。数据库会"跳跃地"通过单个索引,就好像在多个单独的索引中搜索一样。 - 索引聚集因子
索引聚集因子用于衡量某个索引值的行顺序,如员工姓氏。随着顺序程度的升高,聚集因子会降低。
另请参阅:
- Oracle Database SQL Tuning Guide 了解有关索引扫描的详细信息
Full Index Scan
完全索引扫描
在完全索引扫描中,数据库按顺序读取整个索引。如果 SQL 语句中的谓词(WHERE 子句)引用了一个索引列,或者在某些情况下不指定任何谓词,那么就可能使用完全索引扫描。完全扫描可以消除排序,因为数据已经按照索引键排序了。
示例 5-1 完全索引扫描
假设一个应用程序运行以下查询:
SELECT department_id, last_name, salary
FROM employees
WHERE salary > 5000
ORDER BY department_id, last_name;
在这个例子中,department_id、last_name 和 salary 是索引中的复合键。Oracle 数据库对索引执行完全扫描,按照排序顺序(按部门ID和姓氏排序)读取索引,并基于薪水属性进行筛选。这样,数据库只需要扫描一个比employees 表要小的数据集,而不需要扫描那些不包含在查询中的更多列 ,并且避免了对数据进行排序。
完全扫描可能按以下方式读取索引条目:
50, Atkinson, 2800, rowid
60, Austin, 4800, rowid
70, Baer, 10000, rowid
80, Abel, 11000, rowid
80, Ande, 6400, rowid
110, Austin, 7200, rowid
.
.
.
Fast Full Index Scan
快速完全索引扫描
快速完全索引扫描是一种直接访问索引本身的数据而无需访问表的完全索引扫描,数据库并不以特定的顺序读取索引块。
当满足以下两个条件时,快速完全索引扫描是全表扫描的一种替代方法:
- 索引必须包含查询所需的所有列。
- 查询结果集中不能出现所有列都为空值的行。为了保证这一点,索引中至少一个列必须具有以下条件之一:
- NOT NULL 约束
- 用于防止空值被包括在查询结果集中的谓词
示例 5-2 快速完全索引扫描
假设一个应用程序发出以下不包含 ORDER BY 子句的查询:
SELECT last_name, salary
FROM employees;
last_name 列具有非空约束。如果姓和薪水是索引中的复合键,那么快速完全索引扫描可以读取索引条目以获取所请求的信息:
Baida, 2900, rowid
Atkinson, 2800, rowid
Zlotkey, 10500, rowid
Austin, 7200, rowid
Baer, 10000, rowid
Austin, 4800, rowid
.
.
.
Index Range Scan
索引范围扫描
索引范围扫描是索引的有序扫描,其中在条件中指定了索引的一个或多个前导列,并且索引键可能对应 0、1 或多个值。
条件指定一个或多个表达式和逻辑(布尔)运算符的组合。它返回 TRUE、FALSE 或 UNKNOWN 的值。
数据库通常使用索引范围扫描来访问选择性数据。选择性是查询选择的表中行的百分比,其中 0 表示没有行,1 表示所有行。选择性与查询谓词相关,比如 WHERE last_name LIKE ‘A%’,或者谓词的组合。随着值接近 0,谓词变得更具选择性,而随着值接近 1,谓词变得越不具有选择性(或更具有不可选择性)。
例如,用户查询姓氏以 A 开头的员工。假设 last_name 列已建立索引,条目如下:
Abel, rowid
Ande, rowid
Atkinson, rowid
Austin, rowid
Austin, rowid
Baer, rowid
.
.
.
数据库可以使用范围扫描,因为 last_name 列在谓词中指定,并且每个索引键可能有多个行标识。例如,有两个名为 Austin 的员工,因此与键 Austin 相关联的有两个行标识。
索引范围扫描可以在两侧限定,比如查询 ID 在 10 和 40 之间的部门,也可以在一侧限定,比如查询 ID 大于 40 的部门。为了扫描索引,数据库向后或向前移动到叶子块。例如,对于 ID 在 10 和 40 之间的扫描,数据库会定位包含值最低且大于等于 10 的第一个索引叶子块,然后通过扫描叶节点的链接列表水平推进,直到找到一个大于 40 的值。
Index Unique Scan
唯一索引扫描
与索引范围扫描相反,索引唯一扫描必须有0个或1个行标识与索引键相关联。
当谓词使用等号运算符引用唯一索引的键的所有列时,数据库执行唯一扫描。索引唯一扫描在找到第一条记录后立即停止处理,因为不可能有第二条记录。
举例说明,假设用户运行以下查询:
SELECT *
FROM employees
WHERE employee_id = 5;
假设 employee_id 列是主键,并且建立了索引,条目如下:
1, rowid
2, rowid
4, rowid
5, rowid
6, rowid
...
在这种情况下,数据库可以使用索引唯一扫描来定位 ID 为 5 的员工的 rowid。