Index Scans 索引扫描
官方文档链接地址 http://docs.oracle.com/cd/E11882_01/server.112/e40540/indexiot.htm#CNCPT1170
Index Scans
- 在索引扫描中,数据库使用语句指定的索引列,通过遍历索引来检索行。数据库为一个值扫描索引时,发生 n 次 I/O 就能找到其要查找的值,其中 n 即 B-树索引的高度。这是数据库索引背后的基本原理
- 如果 SQL 语句仅访问被索引的列,那么数据库只需直接从索引中读取值,而不用读取表。如果该语句同时还访问除索引列之外的列,那么数据库会使用 rowids 来查找表中的行。通常,为检索表数据,数据库以轮流方式先读取索引块,然后读取相应的表块
Full Index Scan
- 在索引全扫描中,数据库有顺序地读取整个索引。如果在 SQL 语句中的谓词(WHERE 子句)引用了一个索引列,或某些未指定任何谓词的情况,此时可能使用索引全扫描。全扫描可以消除排序,因为数据本身就是基于索引键排过序的
- 假设应用程序运行以下查询:
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
- 快速索引全扫描是一种索引全扫描,数据库仅访问索引本身中的数据,而无需访问表,数据库并不按特定的顺序读取索引块
- 当下面的条件同时满足时,快速完全索引扫描可以替代全表扫描
- 1)索引必须包含查询所需要的所有列
- 2)查询结果集中不会出现全是 NULL 的行,要想保证这一点,至少有一个索引列列符合下面的条件之一:
- NOT NULL 约束
- 应用到该列的谓词阻止 NULL 值作为结果集
- 例如,应用程序发出以下查询,不包含 ORDER BY 子句
SELECT last_name, salary
FROM employees;
- last_name 上有非空约束,last_name 和 salary 是一个复合索引键,那么快速索引全扫描只需读取索引条目,就可以获得需要的信息
Baida,2900,rowid
Zlotkey,10500,rowid
Austin,7200,rowid
Baer,10000,rowid
Atkinson,2800,rowid
Austin,4800,rowid
.
.
.
Index Range Scan
- 索引范围扫描是对索引的有序扫描,具有以下特点:
- 在条件中指定了一或多个索引前导列。条件指定一或多个表达式与逻辑(布尔)运算符的组合,并返回一个值(TRUE/FALSE/UNKNOWN)
- 一个索引键可能对应 0个、1个或多个值
- 通常,数据库使用索引范围扫描来访问具有选择性的数据。选择性是查询所选择的行占总行数的百分比,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 列,而且每个索引键可能对应多个 rowids。例如有两个雇员名叫 Austin,所以有两个 rowids 都与索引键 Austin 相关联
- 索引范围扫描可以在两边都有边界,比如部门 ID 在 10~40 之间的查询,或只在一边有界,比如部门 id > 40 的查询。为扫描索引,数据库将在叶块之间前后移动。例如,对于 ID 在 10~40 之间的扫描,将先定位到包含最低键值(大于或等于10)的第一个索引叶块。然后顺着各个被链接的叶节点水平推进,直到它找到一个大于 40 的值为止
Index Unique Scan
- 相对于索引范围扫描,索引唯一扫描必须有 0或1 个 rowid 与索引键相关联。当一个谓词使用相等运算符引用了唯一索引键的所有列时,数据库将执行唯一扫描。只要找到了第一个记录,索引唯一扫描就停止处理,因为不可能有第二个记录满足条件
- 假设用户运行以下查询:
SELECT *
FROM employees
WHERE employee_id = 5;
- 假定 employee_id 是主键并具有以下的索引条目:
1,rowid
2,rowid
4,rowid
5,rowid
6,rowid
.
.
.
- 在这种情况下,数据库使用索引唯一扫描来定位 ID 是5的雇员的 rowid
Index Skip Scan
- 索引跳跃扫描使用复合索引的逻辑子索引。数据库 "跳跃地" 通过单个索引,就像它是在多个单独的索引中搜索一样。如果在复合索引前导键列中有少量不同值,而在非前导键列中有大量不同值,此时使用跳跃扫描是有益的
- 当在查询谓词中未指定复合索引的前导列时,数据库可能选择索引跳跃扫描。例如,假定你要在 sh.customers 表中查找一个客户,运行如下查询:
SELECT * FROM sh.customers WHERE cust_email = 'Abbey@company.com';
- customers 表中有一列 cust_gender,其值为 M 或F。假定在列 cust_gender 和 cust_email上存在一个复合索引。示例3-1 显示了索引条目的一部分
F,Wolf@company.com,rowid
F,Wolsey@company.com,rowid
F,Wood@company.com,rowid
F,Woodman@company.com,rowid
F,Yang@company.com,rowid
F,Zimmerman@company.com,rowid
M,Abbassi@company.com,rowid
M,Abbey@company.com,rowid
- 即使没有在 WHERE 子句中指定 cust_gender 列,数据库可以使用跳跃索引扫描
- 在跳跃扫描中,逻辑子索引的数目取决于前导列中的非重复值的数目。在示例 3-1 中,前导列中有两个可能值。数据库在逻辑上将该索引拆分为一个具有键 F 的子索引和另一个具有键 M 的子索引
- 当搜索电子邮件为 Abbey@company.com 的客户的记录时, 数据库首先搜索值 F的子索引,然后搜索值 M 的子索引。从概念上讲,数据库按照下面的方式处理查询:
SELECT * FROM sh.customers WHERE cust_gender = 'F'
AND cust_email = 'Abbey@company.com'
UNION ALL
SELECT * FROM sh.customers WHERE cust_gender = 'M'
AND cust_email = 'Abbey@company.com';
Index Clustering Factor
-
索引集群因子用于测量相对于某个索引值(如雇员姓氏)的行的有序度。被索引值的行存储的越有序,则集群因子越低
-
作为一种粗略测量通过索引读取整个表所需的 I/O 数,集群因子非常有用
- 如果集群因子较高,则在大索引范围扫描过程中,数据库将执行相对较高的 I/O。索引条目指向随机的表块,因此数据库可能必须一遍又一遍地来回读索引所指向的同一数据块
- 如果集群因子较低,则在大索引范围扫描过程中数据库将执行相对较低的 I/O。在一个范围内的索引键倾向于指向同一个数据块,因此该数据库不需要来回读同一个数据块
-
集群因子与索引扫描关系密切,因为它可以显示:
- 数据库是否会在大范围扫描中使用索引
- 表相对于索引键的组织度
- 如果行必须按索引键排序,是否应考虑使用索引组织表、分区或表簇
-
例如,假定雇员表存放在两个数据块中。表 3-1 描述了两个数据块中的行(省略号表示未显示的数据)
-
行按姓氏(以粗体显示)顺序存储在块中。例如,数据块 1 的最下面一行描述了 Abel, 往上一行描述了 Ande,如此等等,按字母顺序直到最上面的一行描述了 Steven King。数据块 2 中的最下面行描述了 Kochar,往上一行描述了 Kumar ,如此等等,按字母顺序直到最后一行描述了 Zlotkey
-
假设在姓氏列上存在一个索引。每个姓氏条目对应于一个 rowid。从概念上讲,索引条目看起来如下所示:
Abel,block1row1
Ande,block1row2
Atkinson,block1row3
Austin,block1row4
Baer,block1row5
.
.
.
- 假设在雇员 ID 列上存在另一个单独的索引。从概念上讲,索引条目可能看起来像下面这样,雇员 id 几乎分布在这两个数据块的任意位置
100,block1row50
101,block2row1
102,block1row9
103,block2row19
104,block2row39
105,block1row4
.
.
.
- 示例 3-2 所示的查询,通过 ALL_INDEXES 查看这两个索引的集群因子。EMP_NAME_IX 的集群因子较低,这意味着在同一个叶块中的相邻索引条目倾向于指向同一个数据块中的行。EMP_EMP_ID_PK 的集群因子较高,这意味着同一个叶块中的相邻索引条目不太可能指向同一个数据块中的行
SQL> SELECT INDEX_NAME, CLUSTERING_FACTOR
2 FROM ALL_INDEXES
3 WHERE INDEX_NAME IN ('EMP_NAME_IX','EMP_EMP_ID_PK');
INDEX_NAME CLUSTERING_FACTOR
-------------------- -----------------
EMP_EMP_ID_PK 19
EMP_NAME_IX 2