B-Tree索引
B-Tree是平衡树的缩写,是最常见的数据库索引类型。 B树索引是将不同的值放在一个个范围内的有序列表。通过带有关键字的行或行范围相关联,B树可为广泛的查询(包括精确匹配和范围搜索)提供出色的检索性能。
图形分为两个带有虚线边框的框,一个框在另一个框的上方。顶部标记为“分支块”,下部标记为“叶块”。图形包含一盒树。顶部是一个具有以下值的框:0…40、41…80,依此类推。三个箭头指向分支块的第二行。该行中的一个块的值为1…10、11…19、20…25,依此类推。相邻块的值为41…48、49…53,依此类推。最后一个黑色的值是200…209、210…220,依此类推。
最左边和最右边的块分别具有一对向下的箭头。最左边的箭头指向包含以下值的叶块:0,rowid,0,rowid,10,rowid等。右侧的块包含以下值:11,rowid,11,rowid等。下一个块包含值:221,rowid,222,rowid,依此类推。最右边的块包含以下值:246,rowid,248,rowid等。除最左边和最右边的块外,底行中的每个叶块均通过双向箭头链接到任一侧的块。
B树索引具有两种类型的块:用于搜索的分支块和用于存储值的叶块。 B树索引的上级分支块包含指向下级索引块的索引数据。在上图中,根分支块的条目为0-40,它指向下一分支级别中最左边的块。该分支块包含诸如0-10和11-19之类的条目。这些条目中的每一个都指向一个叶块,其中包含属于该范围的键值。
B树索引是平衡的,因为所有叶块都停留在相同的深度。因此,从索引中任何位置检索任何记录大约需要花费相同的时间。索引的高度是从根块到叶块所需的块数。分支级别是高度减去1。在图中,索引的高度为3,分支级别为2。
分支块存储在两个键之间做出分支决策所需的最小键前缀。这种技术使数据库能够在每个分支块上容纳尽可能多的数据。分支块包含一个指向包含键的子块的指针。键和指针的数量受块大小限制。
叶块包含每个索引数据值和一个用于定位实际行的相应rowid。每个条目按(键,行)排序。在叶块中,键和rowid链接到其左右同级条目。叶子块本身也被双重链接。在图中,最左边的叶子块(0-10)链接到第二个叶子块(11-19)。
Index Scans
在索引扫描中,数据库使用语句指定的索引列的值,遍历索引来检索行。如果数据库扫描索引中的某个值,则它将在n个I / O中找到该值,其中n是B树索引的高度。这是Oracle数据库索引背后的基本原理。
如果SQL语句仅访问索引列,则数据库直接从索引而不是从表读取值。如果该语句除访问索引列之外还访问其他列,则数据库使用rowid在表中查找行。通常,数据库通过轮流读取索引块然后再读取表块来检索表数据。
Full Index Scan
在全索引扫描中,数据库按顺序读取整个索引。如果SQL语句中的谓词(WHERE子句)引用了索引中的列,并且在某些情况下未指定谓词,则可以使用全索引扫描。完全扫描可以消除排序,因为数据是按索引键排序的。
Fast Full Index Scan
快速全索引扫描是一种全索引扫描,其中数据库在不访问表的情况下访问索引本身中的数据,并且数据库不顺序地读取索引块。
如果同时满足以下两个条件,则快速全索引扫描可以替代全表扫描:
- 索引必须包含查询所需的所有列
- 包含所有空值的行不得出现在查询结果集中。为了保证此结果,索引中至少一列必须具有以下任一值:
1.非空约束
2.在查询结果集中,谓词的使用考虑防止空值。
Index Range Scan
索引范围扫描是具有以下特征的索引的有序扫描:
- 在条件中指定索引的一个或多个前导列。条件指定一个或多个表达式和逻辑(布尔)运算符的组合,并返回值TRUE,FALSE或UNKNOWN。
- 具有0,1或者多个值与索引建关联
比如区间查询,between…and,带有头字母的模糊查询 like ‘A%’。
Index Unique Scan
与索引范围扫描相比,索引唯一扫描必须具0个或者1个rowid与索引建关联。当谓词使用等值查询,并涉及UNIQUE索引键中的所有列时,数据库将执行唯一扫描。索引唯一扫描在找到第一条记录后立即停止处理,因为不可能有第二条记录。
Index Skip Scan
index skip scan 使用复合索引的逻辑子索引。数据库“跳过”单个的索引,好像在搜索单独的索引一样。如果在复合索引的前导列中重复值很高,而在索引的非前导列中中唯一值很高,则跳过扫描是有益的。这个读起来比较晦涩,我举个例子
比如
表sh.customers有两列cust_gender, cust_email,并且建了复合索引(cust_gender, cust_email),但是查询条件没有前导列,而且cust_gender只有F或者M,但是cust_email基本是唯一的,复合上面描述的情况,那么这里会使用index skip scan。
F,Wolf@,rowid
F,Wolsey@,rowid
F,Wood@,rowid
F,Woodman@,rowid
F,Yang@,rowid
F,Zimmerman@,rowid
M,Abbassi@,rowid
M,Abbey@,rowid
这里解释一下
数据库首先搜索值为F的子索引,然后搜索值为M的子索引。从概念上讲,数据库按以下方式处理查询:
SELECT * FROM sh.customers WHERE cust_gender = 'F'
AND cust_email
UNION ALL
SELECT * FROM sh.customers WHERE cust_gender = 'M'
AND cust_email
Index Clustering Factor
索引聚类因子衡量与索引值相关的行顺序。行存储中存在的这个值的顺序值越高,聚簇因子越低。聚簇因子是基于表上索引列上的一个值,每一个索引都有一个聚簇因子。用于描述索引块上与表块上存储数据在顺序上的相似程度,也就说表上的数据行的存储顺序与索引列上顺序是否一致。如果基本一致,那么聚簇因子就比较低,如果顺序不一致,那么聚簇因子就高
聚集因子可作为通过一个索引读取整个表粗略计算I/O数量的有用方法:
如果集群因子很高,则Oracle数据库在索引大范围扫描期间将执行相对大量的I / O。索引条目指向随机表块,因此数据库可能不得不一次又一次地读取和重新读取相同的块,以检索索引所指向的数据。
如果集群因子较低,则Oracle数据库在索引大范围扫描期间将执行相对较少的I / O。范围中的索引键往往指向同一数据块,因此数据库不必一遍又一遍地读取和重新读取相同的块。
聚类因子与索引扫描有关,因为它可以显示:
数据库是否将索引用于大范围扫描
表组织程度与索引键的关系
如果必须通过索引键对行进行排序,则是否应考虑使用索引组织的表,分区或表集群
假设在姓氏列上存在索引。每个名称条目都对应一个rowid。从概念上讲,索引条目将如下所示:
假设在员工ID列上有一个单独的索引。从概念上讲,索引条目可能如下所示,在两个区域中,员工ID分布在几乎随机的位置:
以下ALL_INDEXES视图中查询这两个索引的聚类因子。 EMP_NAME_IX的聚类因子很低,这意味着单个叶块中的相邻索引条目往往指向同一数据块中的行。 EMP_EMP_ID_PK的聚类因子很高,这意味着同一叶块中的相邻索引条目不太可能指向同一数据块中的行。
Reverse Key Indexes
反向键索引是一种B树索引,它在保持列顺序的同时物理地反转每个索引键的字节。例如,如果索引关键字是20,并且在标准B树索引中以十六进制形式为此关键字存储的两个字节为C1,15,则反向关键字索引将字节存储为15,C1。
反转键解决了B树索引右侧的叶子块争用的问题。在多个实例重复修改同一块的Oracle Real Application Clusters(Oracle RAC)数据库中,此问题尤其严重。例如,在订单表中,订单的主键是顺序的。集群中的一个实例添加顺序20,而另一个实例添加21,每个实例将其键写入索引右侧的同一叶块。
在反向键索引中,字节顺序的反向将插入内容分布在索引中的所有叶键之间。例如,现在将在标索引中相邻的诸如20和21之类的密钥存储在分开的块中。因此,用于顺序键插入的I / O分布更均匀。
由于索引中的数据在存储时没有按列键进行排序,因此在某些情况下,反向键排列消除了运行索引范围扫描查询的能力。例如,如果用户发出的订单ID大于20的查询,则数据库无法从包含该ID的块开始,而无法水平浏览叶块。
Ascending and Descending Indexes
在升序索引中,Oracle数据库按升序存储数据。默认情况下,字符数据按值的每个字节中包含的二进制值排序,数值从最小到最大,日志为从最早的日期到最新的日期排序。
也可以指定降序
CREATE INDEX emp_name_dpt_ix ON hr.employees(last_name ASC, department_id DESC);
Key Compression
Oracle数据库可以使用键压缩来压缩B树索引类型的主键部分或索引组织表。键压缩可以大大减少索引占用的空间。
通常,索引键有两个部分,分组部分和唯一部分。键压缩将索引键分为一个前缀条目(即分组)和一个后缀条目(即唯一或几乎唯一)。数据库通过在索引块的后缀条目之间共享前缀条目来实现压缩。
举例:
CREATE INDEX orders_mod_stat_ix ON orders ( order_mode, order_status );
下图,许多重复的值出现在order_mode和order_status列中。索引块可能具有如示例3-3中所示的条目。
在示例3-3中,键前缀将由order_mode和order_status值的串联组成。如果此索引是使用默认键压缩创建的,则将压缩重复的键前缀,例如online,0和online,2。从概念上讲,数据库实现了压缩,如以下示例所示:
或者,您可以在创建压缩索引时指定前缀长度。例如,如果您指定前缀长度1,则前缀将为order_mode,后缀为order_status,rowid。对于示例3-3中的值,该索引将排除重复出现的在线事件,如下所示:
Bitmap Indexes
在位图索引中,数据库为每个索引键存储一个位图。在常规的B树索引中,一个索引条目指向单个行。在位图索引中,每个索引键都存储指向多行的指针。
位图索引的设计主要用于数据仓库或查询以临时方式引用许多列的环境。可能需要位图索引的情况包括:
- 索引列选择性很低,也就是说,与表行的数量相比,不同值的数量少。
- 相关表要么是只读的,要么不要有频繁DML操作。
Bitmap Indexes on a Single Table
如下是一张客户表
cust_marital_status和cust_gender列的选择性较低,而cust_id和cust_last_name则高。因此,位图索引适用于cust_marital_status和cust_gender。位图索引可能对其他列没有用。相反,这些列上的唯一B树索引可能会提供最有效的表示和检索。
表3-2说明了示例3-4中显示的cust_gender列输出的位图索引。它包含两个单独的位图,每个性别一个。
映射函数将位图中的每个位转换为客户表的rowid。每个位的值取决于表中相应行的值。例如对于M值的位图包含1作为其第一位,因为在客户表的第一行中性别为M。位图cust_gender ='M’的第2、6和7行的位为0,因为这些行性别不是M。
位图索引可以包含完全由空值组成的键,这与B树索引不同。为某些SQL语句(例如,使用聚合函数COUNT的查询),索引空值可能很有用。
调查客户的人口趋势的分析师可能会问:“我们有多少女性客户是单身或离婚的?”此问题对应于以下SQL查询:
SELECT COUNT(*)
FROM customers
WHERE cust_gender = 'F'
AND cust_marital_status IN ('single', 'divorced');
位图索引可以有效地合并与WHERE子句中的多个条件相对应的索引。在访问表本身之前,将过滤满足某些(但不是全部)条件的行。此项技术可以显著的缩短响应时间。
Bitmap Join Indexes
是为了连接两个或者更多的表的位图索引,为表中列的每一个值,索引存储在索引表中相应行的rowid。
位图连接索引是通过预先执行限制减少数据量的有效方法。图下面例子,假设用户经常查询具有特定工作类型的员工人数。查询可能如下所示:
SELECT COUNT(*)
FROM employees, jobs
WHERE employees.job_id = jobs.job_id
AND jobs.job_title = 'Accountant';
上面的查询通常会在jobs.job_title上使用索引来获取Accountant的行,然后job_id中使用,然后在employee.job_id上使用索引来查找匹配的行。要从索引本身而不是从表扫描中检索数据,你可以按以下方式创建位图联接索引:
CREATE BITMAP INDEX employees_bm_idx
ON employees (jobs.job_title)
FROM employees, jobs
WHERE employees.job_id = jobs.job_id;
从概念上来说,employees_bm_idx是示例3-5中所示的SQL查询中的jobs.title列的索引)。索引中的job_title键指向employees表中的行。查询会计师人数可以使用索引来避免访问employees和jobs表,因为索引本身包含了所请求的信息。
在数据仓库中,联接条件是度量表的主键列与事实表的外键列之间的等值连接(使用相等运算符)。有时位图联接索引在存储方面比雾化联接视图要高效得多。
Bitmap Storage Structure
Oracle数据库使用B树索引结构来存储每个索引键的位图。例如,如果Jobs.job_title是位图索引的关键字列,则索引数据存储在一个B树中。各个位图存储在叶块中。
假设jobs.job_title列具有唯一值“Shipping Clerk”,“Stock Clerk”其他一些其他值。该索引的位图索引条目包含以下组件:
- job_title作为索引键
- 一个低的rowid和一个高的rowid可以作为rowid的一个范围
- 在范围内的特定的rowid的位图
概念上来说,此索引中的索引叶块可以包含如下条目:
由于rowid范围不同,因此同一职位出现在多个条目中。
位图索引的数据存储在一个段中。 Oracle数据库将每个位图存储为一个或多个片中。每一片占用单个数据块的一部分。
Function-Based Indexes
可以在表中的一个或多个列的函数和表达式上创建索引。函数索引计算涉及一个或多个列的函数或表达式的值,并将其存储在索引中。函数索引可以是B树或位图索引。
如下所示:
创建一个带计算表达式的复合索引
CREATE INDEX emp_total_sal_idx
ON employees (12 * salary * commission_pct, salary, commission_pct);
查询条件包含一个计算表达式
SELECT employee_id, last_name, first_name,
12*salary*commission_pct AS "ANNUAL SAL"
FROM employees
WHERE (12 * salary * commission_pct) < 30000
ORDER BY "ANNUAL SAL" DESC;
EMPLOYEE_ID LAST_NAME FIRST_NAME ANNUAL SAL
----------- ------------------------- -------------------- ----------
159 Smith Lindsey 28800
151 Bernstein David 28500
152 Hall Peter 27000
160 Doran Louise 27000
175 Hutton Alyssa 26400
149 Zlotkey Eleni 25200
169 Bloom Harrison 24000
使用函数的例子:
创建函数索引
CREATE INDEX emp_fname_uppercase_idx
ON employees ( UPPER(first_name) );
下面查询可以使用到函数索引
SELECT *
FROM employees
WHERE UPPER(first_name) = 'AUDREY';
函数索引对于仅索引表中的特定行也很有用。例如,sh.customers表中的cust_valid列具有I或A作为值。要仅索引A行,您可以编写一个函数,为A行以外的任何行返回空值。您可以按如下方式创建索引:
CREATE INDEX cust_valid_idx
ON customers ( CASE cust_valid WHEN 'A' THEN 'A' END );
Optimization with Function-Based Indexes
优化器可以对基于函数索引使用索引范围扫描,以WHERE子句中具有表达式的查询为例。当谓词(WHERE子句)具有较低选择性时,范围扫描访问路径特别有用。在以上表达式示例中,如果在表达式12 * salary * commission_pct上建立索引,则优化器可以使用索引范围扫描。
虚拟列对于加快对从表达式派生的数据的访问很有用。例如,您可以将虚拟列annual_sal定义为12 * salary * commission_pct,并在annual_sal上创建基于函数的索引。
Application Domain Indexes
应用域索引是特定于应用程序的自定义索引。 Oracle数据库提供了可扩展的索引来执行以下操作:
-
向用户提供有关复杂数据类型的索引的定义,例如文档,空间数据,图像和视频剪辑
-
利用专业的索引技术
您可以将特定于应用域索引管理例程封装为索引类型架构对象,并在表列或对象类型的属性上定义域索引。扩展索引可以有效地处理特定于应用的运算。
应用软件控制域索引的结构和内容,这个应用软件称为cartridge。数据库与应用互相构建,维护和搜索域索引。索引结构本身可以作为索引组织表存储在数据库中,也可以作为文件存储在操作系统中。
Index Storage
Oracle将索引数据存储在索引段中。数据块中可用于索引数据的空间是数据块大小减去块头,条目头,rowid和每个值的索引的一个长度的字节。
索引表空间可以是默认表空间,也可以用CREATE INDEX中专门命名的表空间。为了便于管理,您可以将索引存储在单独的表空间中。你可以不备份只放索引的表空间,这样备份可以缩短时间,也可以节省备份空间