ORACLE 索引概述

时间:2021-11-17 10:10:17

索引是数据库中一种可选的数据结构,她通常与表或簇相关。用户可以在表的一列或数列上建立索引,以提高在此表上执行 SQL 语句的性能。就像本文档的索引可以帮助读者快速定位所需信息一样,Oracle 的索引提供了更为迅速地访问表数据的方式。正确地使用索引能够显著的减少磁盘 I/O。

用户可以为一个表创建多个索引,只要不同索引使用的列或列的组合(combination of columns)不同即可。例如,下列语句中指定的列组合是有效的:

CREATE INDEX employees_idx1 ON employees (last_name, job_id);

CREATE INDEX employees_idx2 ON employees (job_id, last_name);

Oracle 提供了各种类型的索引,她们能够互为补充地提升查询性能:

  • 平衡树索引(B-tree index)
  • 平衡树簇索引(B-tree cluster index)
  • 哈希簇索引(hash cluster index)
  • 反向键索引(reverse key indexes)
  • 位图索引(bitmap index)
  • 位图连接索引(bitmap join index)

Oracle 还支持函数索引(function-based index),以及针对特定应用程序或程序模块(cartridge)的域索引(domain index)。

无论索引是否存在都无需对已有的 SQL 语句进行修改。索引只是提供了一种快速访问数据的路径,因此她只会影响查询的执行速度。当给出一个已经被索引的数据值后,就可以通过索引直接地定位到包含此值的所有数据行。

索引在逻辑上和物理上都与其基表(base table)是相互独立的。用户可以随时创建(create)或移除(drop)一个索引,而不会影响其基表或基表上的其他索引。当用户移除一个索引时,所有的应用程序仍然能够继续工作,但是数据访问速度有可能会降低。作为一种独立的数据结构,索引需要占用存储空间。

当索引被创建后,对其的维护与使用都是 Oracle 自动完成的。当索引所依赖的数据发生插入,更新,删除等操作时,Oracle 会自动地将这些数据变化反映到相关的索引中,无需用户的额外操作。

即便索引的基表中插入新的数据,对被索引数据的查询性能基本上能够保持稳定不变。但是,如果在一个表上建立了过多的索引,将降低其插入,更新,及删除的性能。因为 Oracle 必须同时修改与此表相关的索引信息。

优化器可以使用已有的索引来建立(build)新的索引。这将加快新索引的建立速度。

5.8.1 唯一索引和非唯一索引

索引(index)可以是唯一(unique)的或非唯一(nonunique)的。在一个表上建立唯一索引(unique index)能够保证此表的索引列(一列或多列)不存在重复值。而非唯一索引(nonunique index)并不对索引列值进行这样的限制。
        Oracle 建议使用 CREATE UNIQUE INDEX 语句显式地创建唯一索引(unique index)。通过主键(primary key)或唯一约束(unique constraint)来创建唯一索引不能保证创建新的索引,而且用这些方式创建的索引不能保证为唯一索引。

5.8.2 复合索引

复合索引(composite index)(也被称为连结索引(concatenated index))是指创建在一个表的多列上的索引。复合索引内的列可以任意排列,她们在数据表中也无需相邻。

如果一个 SELECT 语句的 WHERE 子句中引用了复合索引(composite index)的全部列(all of the column)或自首列开始且连续的部分列(leading portion of the column),将有助于提高此查询的性能。因此,索引定义中列的顺序是很重要的。大体上说,经常访问的列(most commonly accessed)或选择性较大的列(most selective)应该放在前面。

一个常规的(regular)复合索引(composite index)不能超过 32 列,而位图索引(bitmap index)不能超过 30 列。索引中一个键值(key value)的总长度大致上不应超过一个数据块(data block)总可用空间的一半。

5.8.3 索引和键

索引(index)与键(key)是连个不同的概念,但是这两个术语经常被混用。索引是在数据库中实际存储的数据结构,用户可以使用 SQL 语句对其进行创建(create),修改(alter),或移除(drop)。索引提供了一种快速访问表数据的途径。而键只是一个逻辑概念。键的概念主要在 Oracle 的完整性约束(integrity constraint)功能中使用,完整性约束用于保证数据库中的业务规则(business rule)。

因为 Oracle 也会使用索引(index)来实现某些完整性约束(integrity constraint),因此索引与键(key)这两个术语经常被混用。注意不要将二者混淆。

5.8.4 索引和空值

对于一个数据表的两行或多行,如果其索引列(key column)中全部非空(non-NULL)的值完全相同(identical),那么在索引中这些行将被认为是相同的;反之,在索引中这些行将被认为是不同的。因此使用 UNIQUE 索引可以避免将包含 NULL 的行视为相同的。以上讨论并不包括索引列的列值(column value)全部为 NULL 的情况。

Oracle 不会将索引列(key column)全部为 NULL 的数据行加入到索引中。不过位图索引(bitmap index)是个例外,簇键(cluster key)的列值(column value)全部为 NULL 时也是例外。

5.8.5 函数索引

如果一个函数(function)或表达式(expression)使用了一个表的一列或多列,则用户可以依据这些函数或表达式为表建立索引,这样的索引被称为函数索引(Function-Based Index)。函数索引能够计算出函数或表达式的值,并将其保存在索引中。用户创建的函数索引既可以是平衡树类型(B-tree index)的,也可以是位图类型(bitmap index)的。

用于创建索引的函数可以是一个数学表达式(arithmetic expression),也可以是使用了 PL/SQL 函数(PL/SQL function),包函数(package function),C 外部调用(C callout),或 SQL 函数(SQL function)的表达式。用于创建索引的函数不能包含任何聚合函数(ggregate function),如果为用户自定义函数,则在声明中必须使用 DETERMINISTIC关键字。如果在一个使用对象类型(object type)的列上建立函数索引,则可以使用此对象的方法(method)作为函数,例如此对象的 map 方法。用户不能在数据类型为 LOB,REF,或嵌套表(nested table)的列上建立函数索引,也不能在包含 LOB,REF,或嵌套表等数据类型的对象类型列上建立函数索引。

5.8.5.1 使用函数索引

如果一个 SQL 语句的 WHERE 子句中使用了函数,那么建立相应的函数索引(function-based index)是提高数据访问性能的有效机制。表达式(expression)的结果经过计算后将被存储在索引中。但是当执行 INSERT 和 UPDATE 语句时,Oracle 需要进行函数运算以便维护索引。

例如,如果用户创建了以下函数索引:

CREATE INDEX idx ON table_1 (a + b * (c – 1), a, b);

当 Oracle 处理如下查询时就可以使用之前建立的索引:

SELECT a FROM table_1 WHERE a + b * (c – 1) < 100;

使用 UPPER(column_name) 或 LOWER(column_name) 函数建立函数索引(function-based index)有助于与大小写无关(case-insensitive)的查询。例如创建以下函数索引:

CREATE INDEX uppercase_idx ON employees (UPPER(first_name));

有助于提高以下查询的性能:

SELECT * FROM employees WHERE UPPER(first_name) = ‘RICHARD’;

5.8.5.2 函数索引的优化

用户必须为优化器(optimizer)收集关于函数索引(unction-based index)的统计信息(statistic)。否则处理 SQL 语句时将不会使用此索引。

当一个查询的 WHERE 子句中含有表达式(expression)时,优化器可以对函数索引(function-based index)进行索引区间扫描(index range scan)。例如以下查询:

SELECT * FROM t WHERE a + b < 10;

如果使用表达式(expression) a+b 建立的索引,优化器(optimizer)就能够进行索引区间扫描(index range scan)。如果谓词(predicate,即 WHERE 子句)产生的选择性(selectivity)较低,则对区间扫描极为有利。此外,如果表达式的结果物化在函数索引内(function-based index),优化器将能更准确地估计使用此表达式的谓词的选择性。

优化器(optimizer)能够将 SQL 语句及函数索引(function-based index)中的表达式解析为表达式树(expression tree)并进行比较,从而实现表达式匹配。这个比较过程是大小写无关的(case-insensitive),并将忽略所有空格(blank space)。

5.8.5.3 函数索引的依赖性

函数索引(function-based index)依赖于索引定义表达式中使用的函数。如果此函数为 PL/SQL 函数(PL/SQL function)或包函数(package function),当函数声明(function specification)发生变化时,索引将失效(disabled)。

用户需要被授予(grant)CREATE INDEX 或 CREATE ANY INDEX 权限才能创建函数索引(function-based index)。

要想使用函数索引(function-based index):

  • 建立索引后,表必须经过分析(analyze)。
  • 必须保证查询的条件表达式不是 NULL 值, 因为 NULL 值不会被存储到索引中。

以下各节将讲述使用函数索引的其他需求。

5.8.5.3.1 DETERMINISTIC 函数

函数索引(function-based index)使用的用户自定义函数(user-written function)必须声明为 DETERMINISTIC,此关键字表明对于一定的输入参数,此函数总会得到相同的输出结果。

5.8.5.3.2 定义函数的权限

函数索引(function-based index)的所有者(owner)必须具备此索引定义中使用的函数的EXECUTE 权限。当 EXECUTE 权限被收回(revoke)后,Oracle 则将索引标识为 DISABLED。索引的所有者无须具备此函数的 EXECUTE WITH GRANT OPTION 权限,即可将索引所在表的SELECT 权限授予(grant)其他用户。

5.8.5.3.3 解决函数索引的依赖性问题

函数索引(function-based index)依赖于她使用的所有函数。如果函数或函数所在包的声明(specification)被修改过(或索引所有者对函数的 EXECUTE 权限被收回),将会出现以下情况:

  • 索引被标记为 DISABLED。
  • 如果优化器(optimizer)选择了在标记为 DISABLED 的索引上执行查询,那么此查询将失败
  • 使用标记为 DISABLED 的索引而执行的 DML 操作将失败,除非此索引同时被标记为UNUSABLE 且初始化参数(initialization parameter) SKIP_UNUSABLE_INDEXES 被设为TRUE。

函数被修改之后,用户可以使用 ALTER INDEX … ENABLE 语句将索引重新置为 ENABLE 状态。

5.8.6 索引是如何存储的

当用户创建索引时,Oracle 会自动地在表空间(tablespace)中创建索引段(index segment)来存储索引的数据。用户可以通过以下方式控制索引段的空间分配和使用:

  • 设置索引段的存储参数(storage parameter)来控制如何为此索引段分配数据扩展(extent)
  • 为索引段设置 PCTFREE 参数,来控制组成数据扩展的各个数据块(data block)的可用空间情况。

索引段(index segment)使用的表空间(tablespace)既可以是索引所有者(owner)的默认表空间,也可以是在 CREATE INDEX 语句中指定的表空间。索引无需和其相关的表位于同一表空间中。相反,如果将索引与其相关表存储在不同磁盘上能够提升使用此索引的查询性能,因为此时 Oracle 能够并行地(parallel)访问索引及表数据。

5.8.6.1 索引块的格式

一个数据块(data block)内可用于存储索引数据的空间等于数据块容量减去数据块管理开销(overhead),索引条目管理开销(entry overhead),rowid,及记录每个索引值长度的 1 字节(byte)。

当用户创建索引时,Oracle 取得所有被索引列的数据并进行排序,之后将排序后索引值和与此值相对应的 rowid 按照从下到上的顺序加载到索引中。例如,以下语句:

CREATE INDEX employees_last_name ON employees(last_name);

Oracle 先将 employees 表按 last_name 列排序,再将排序后的  列及相应的 rowid 按从下到上的顺序加载到索引中。使用此索引时,Oracle 可以快速地搜索已排序的 last_name 值,并使用相应的 rowid 去定位包含用户所查找的 last_name 值的数据行。

5.8.6.2 索引的内部结构

Oracle 使用平衡树(B-tree)存储索引以便提升数据访问速度。当不使用索引时,用户必须对数据进行顺序扫描(sequential scan)来查找指定的值。如果有 n 行数据,那么平均需要扫描的行为 n/2。因此当数据量增长时,这种方法的开销将显著增长。

如果将一个已排序的值列(list of the values)划分为多个区间(range),每个区间的末尾包含指向下个区间的指针(pointer),而搜索树(search tree)中则保存指向每个区间的指针。此时在 n 行数据中查询一个值所需的时间为 log(n)。这就是 Oracle 索引的基本原理。

在一个平衡树索引(B-tree index)中,最底层的索引块(叶块(leaf block))存储了被索引的数据值,以及对应的 rowid。叶块之间以双向链表的形式相互连接。位于叶块之上的索引块被称为分支块(branch block),分枝块中包含了指向下层索引块的指针。如果被索引的列存储的是字符数据(character data),那么索引值为这些字符数据在当前数据库字符集(database character set)中的二进制值(binary value)。

对于唯一索引(unique index),每个索引值对应着唯一的一个 rowid。对于非唯一索引(nonunique index),每个索引值对应着多个已排序的 rowid。因此在非唯一索引中,索引数据是按照索引键(index key)及 rowid 共同排序的。键值(key value)全部为 NULL 的行不会被索引,只有簇索引(cluster index)例外。在数据表中,如果两个数据行的全部键值都为 NULL,也不会与唯一索引相冲突。

5.8.6.3 索引的属性

有两种类型的索引块:

  • 用于搜索的分支块(branch block)
  • 用于存储索引数据的叶块(leaf block)

5.8.6.3.1 分支块

分支块(branch block)中存储以下信息:

  • 最小的键值前缀(minimum key prefix),用于在(本块的)两个键值之间做出分支选择
  • 指向包含所查找键值的子块(child block)的指针()

包含 n 个键值的分支块(branch block)含有 n+1 个指针。键值及指针的数量同时还受索引块(index block)容量的限制。

5.8.6.3.2 叶块

所有叶块(leaf block)相对于其根分支块(root branch block)的深度(depth)是相同的。叶块用于存储以下信息:

  • 数据行的键值(key value)
  • 键值对应数据行的 ROWID
所有的 键值-ROWID 对(key and ROWID pair)都与其左右的兄弟节点(sibling)向链接(link),并按照(key,ROWID)的顺序排序。

5.8.6.4 平衡树结构的优势

平衡树数据结构(B-tree structure)具有以下优势:

  • 平衡树(B-tree)内所有叶块(leaf block)的深度相同,因此获取索引内任何位置的数据所需的时间大致相同。
  • 平衡树索引(B-tree index)能够自动保持平。
  • 平衡树内的所有块容量平均在总容量的 3/4 左右。
  • 在大区间(wide range)范围内进行查询时,无论匹配个别值(exact match)还是搜索一个区间(range search),平衡树都能提供较好的查询性能。
  • 数据插入(insert),更新(update),及删除(delete)的效率较高,且易于维护键值的顺序(key order)
  • 大型表,小型表利用平衡树进行搜索的效率都较好,且搜索效率不会因数据增长而降低。

5.8.7 索引唯一扫描

索引唯一扫描(index unique scan)是效率最高的数据访问方式之一。从平衡树索引(B-tree index)中获取数据时将采用此种方式。当一个唯一索引(采用平衡树结构)的全部列都包含在查询条件中,且查询体条件表达式均为等号(equality)时,优化器将选择使用索引唯一扫描。

5.8.8 索引区间扫描

当访问选择性较大的数据(selective data)时 Oracle 常进行索引区间扫描(index range scan)。扫描区间可以是封闭的(bounded)(两端均封闭),也可以是不封闭的(unbounded)(一端或两端均不封闭)。扫描所返回的数据按照索引列的升序进行排列,对于索引值相同的行将按 ROWID 的升序排列。

5.8.9 键压缩

用户利用键压缩(key compression)可以将索引或索引表(index-organized table)中键值(column value)的部分内容进行压缩,以便减少重复值带来的存储开销。

一般来说,索引的一个键(key)通常由两个片段(piece)构成:分组片段(grouping piece)及唯一片段(unique piece)。如果定义索引的键中不存在唯一片段,Oracle 会以 ROWID 的形式在此键的分组片段后添加一个唯一片段。键压缩(key compression)就是将键的分组片段从键中拆分出来单独存储,供多个唯一片段使用。

5.8.9.1 索引键的前缀和后缀

键压缩(key compression)将一个索引键拆分为前缀(prefix entry)(即分组片段(grouping piece))和后缀(suffix entry)(即唯一片段(unique piece))。压缩是通过一个索引块(index block)中的多个后缀共享一个前缀来实现的。在平衡树索引(B-tree index)中只有位于叶块(leaf block)的键会被压缩。在分支块(branch block)内不必存储键的后缀,因此其中的键也无需压缩。

键压缩(key compression)只能在每个索引块(index block)内分别实现,而不能跨多个索引块。压缩后每个索引行(index row)只保存后缀(suffix entry),而每个后缀将引用一个共享的前缀(prefix entry),后缀与其共享的前缀必须位于同一索引块内。

默认情况下,前缀(prefix entry)由除去最后一列之外的其他键列(key column)构成。例如,一个索引键(index key)由(column1,column2,column3)3 列构成,则默认的前缀为(column1, column2)。如一组索引值为(1,2,3),(1,2,4),(1,2,7),(1,3,5),(1,3,4),(1,4,4),则其中重复出现的前缀 (1,2),(1,3) 将被压缩。

用户也可以手工设定前缀长度(prefix length),即前缀所包含的列数。例如,如果用户设定前缀长度为 1,则在上述例子中,column1 为前缀,(column2,column3)为后缀,其中重复出现的前缀 1 将被压缩。

非唯一索引(nonunique index)的最大前缀长度(prefix length)为键列的个数,而唯一索引(unique index)的最大前缀长度为键列的个数减 1。

应用键压缩(key compression)后,生成索引时,如果一个键值(key value)的前缀(prefix entry)在索引块(index block)中不存在,此前缀才会被写入索引块中。一个前缀被写入后立即就可以被此索引块内的后缀(suffix entry)共享,直到所有引用此前缀的后缀都被删除为止。

5.8.9.2 性能上及存储上的考虑

键压缩(key compression)能够节约大量存储空间,因此用户可以在一个索引块(index block)内存储更多的索引键(index key),从而减少 I/O,提高性能。

键压缩(key compression)能够减少索引所需的存储空间,但索引扫描时需要重构(reconstruct)键值(key value),因此增加了 CPU 的负担。此外键压缩也会带来一些存储开销,每个前缀(prefix entry)需要 4 字节(byte)的管理开销。

5.8.9.3 使用键压缩

键压缩(key compression)在多种情况下都能够发挥作用,例如:

  • 对于非唯一索引(nonunique index),Oracle 会在每个重复的索引键(index key)之后添加 rowid 以便区分。如果使用了键压缩,在一个索引块(index block)内,Oracle 只需将重复的索引键作为前缀((prefix entry))存储一次,并用各行的 rowid 作为后缀(suffix entry)。
  • 唯一索引(nonunique index)中也存在相同的情况。例如唯一索引(stock_ticker,transaction_time)的含义是(项目,时间戳),通常数千条记录中stock_ticker 的值是相同的,但她们对应的 transaction_time 值各不相同。使用了键压缩后,一个索引块中每个 stock_ticker 值作为前缀只需存储一次,而各个transaction_time 值则作为后缀存储,并引用一个共享的 stock_ticker 前缀。
  • 在一个包含 VARRAY 或 NESTED TABLE 数据类型(datatype)的索引表(index-organized table)中,这些collection 类型中各个元素(element)的对象标识符(object identifier)是重复的。用户可以使用键压缩以避免重复存储这些对象标识符。

有些情况无法使用键压缩(key compression)。例如,一个只有一个索引键(index key)的唯一索引(unique index)就无法使用键压缩,因为索引键中不存在可供共享的分组片段(grouping piece)。

5.8.10 逆序键索引

用户可以创建逆序键索引(reverse key index),此处的逆序指索引列值(index key value)得各个字节(byte)按倒序排列,而非索引列(index key)逆序排列。在 RAC 环境中,使用这样的排列方式可以避免由于对索引的修改集中在一小部分叶块(leaf block)上而造成的性能下降。通过使索引的键值逆序排列,可以使插入操作分布在索引的全部叶块中。

使用逆序键索引(reverse key index)后将无法对此索引进行索引区间扫描(index range scanning),因为在逆序键索引 中,词汇上(lexically)相邻的索引键(index key)在存储上未必相邻。因此在逆序键索引 上只能进行确定键扫描(fetch-by-key scan)或全索引扫描(full-index scan)。

有些情况下,使用逆序键索引(reverse key index)可以令 RAC 环境下的 OLTP 应用效率更高。例如,为一个 e-mail 应用中的所有邮件进行索引:由于用户可能保存旧的邮件,因此索引必须做到既能快速访问最新邮件,也能快速访问旧邮件。

用户使用 REVERSE 就可以轻易地创建逆序键索引(reverse key index)。在 CREATE INDEX语句中使用 REVERSE 关键字作为创建索引的选项:

CREATE INDEX i ON t (a,b,c) REVERSE;

用户也可以在 REBUILD 子句后添加 NOREVERSE 关键字将一个逆序键索引(reverse key index)转换为常规的索引:

ALTER INDEX i REBUILD NOREVERSE;

如果 REBUILD 子句后没有使用 NOREVERSE 关键字,那么逆序键索引(reverse key index)被重建后仍将保持逆序。

5.8.11 位图索引

索引的目标是为用户提供指向包含特定键值(key value)的数据行的指针。在常规的索引中,Oracle 将各行的键值及与此键值对应的一组 ROWID 存储在一起,从而实现了上述目标。而在位图索引(bitmap index)中,只需存储每个键值的位图(bitmap),而非一组 ROWID。

位图(bitmap)中的每一位(bit)对应一个可能的 ROWID。如果某一位被置位(set),则表明着与此位对应的 ROWID 所指向的行中 包含此位所代表的键值(key value)。Oracle 通过一个映射函数(mapping function)将位信息转化为实际的 ROWID,因此虽然位图索引(bitmap index)内部的存储结构与常规索引不同,但她同样能实现常规索引的功能。当不同值的索引键的数量较少时,位图索引的存储效率相当高。

如果在 WHERE 子句内引用的多个列上都建有位图索引(bitmap index),那么进行位图索引扫描时(bitmap indexing)可以将各个位图索引融合在一起。不满足全部条件的行可以被预先过滤掉。因此使用位图索引能够极大地提高查询的响应时间。

5.8.11.1 数据仓库应用中位图索引的优势

数据仓库应用(data warehousing application)的特点是数据量巨大,执行的多为自定义查询(ad hoc query),且并发事务较少。这种环境下使用位图索引(bitmap index)具备如下优势:

  • 能够减少大数据量自定义查询的响应时间
  • 与其他索引技术相比能够节省大量存储空间
  • 即使硬件配置较低也能显著提高性能
  • 有利于并行 DML 和并行加载

为一个大表建立传统的平衡树索引(B-tree index)可能占用极大的存储空间,索引有可能比数据表还要大数倍。而一个位图索引(bitmap index)所占的空间比被索引数据还要小得多。

位图索引(bitmap index)不适用于 OLTP 系统,因为这样的系统中存在大量对数据进行修改的并发事务。位图索引主要用于数据仓库系统中(data warehousing)的决策支持功能,在这种环境下用户对数据的操作主要是查询而非修改。

主要进行大于(greater than)或小于(less than)比较的列,不适宜使用位图索引(bitmap index)。例如,WHERE 子句中常会将 salary 列和一个值进行比较,此时更适合使用平衡树索引(B-tree index)。位图索引适用于等值查询,尤其是存在 AND,OR,和 NOT 等逻辑操作符的组合时。

位图索引(bitmap index)是集成在 Oracle 的优化器(optimizer)和执行引擎(execution engine)之中的。位图索引也能够和 Oracle 中的其他执行方法(execution method)无缝地组合。例如,优化器可以在利用一个表的位图索引和另一个表的平衡树索引(B-tree index)对这两张表进行哈希连接(hash join)。优化器能够在位图索引及其他可用的访问方法(例如常规的平衡树索引,或全表扫描(full table scan))中选择效率最高的方式,同时考虑是否适合使用并行执行。

位图索引(bitmap index)如同常规索引一样,可以结合并行查询(parallel query)和并行 DML(parallel DML)一起工作。建立于分区表(partitioned table)的位图索引必须为本地索引(local index)。Oracle 还支持并行地创建位图索引,以及创建复合位图索引。

5.8.11.2 基数

在基数(cardinality)小的列上建立位图索引(bitmap index)效果最好。所谓某列的基数小(low cardinality)是指此列中所有不相同的值的个数要小于总行数。如果某列中所有不相同的值的个数占总行数的比例小于 1%,或某列中值的重复数量在 100 个以上,那么就可以考虑在此列上建立位图索引。即便某列的基数较上述标准稍大,或值的重复数量较上述标准稍小,如果在一个查询的 WHERE 子句中需要引用此列定义复杂的条件,也可以考虑在此列上建立位图索引。

例如,一个表包含一百万行数据,其中的一列包含一万个不相同的值,就可以考虑在此列上创建位图索引(bitmap index)。此列上位图索引的查询性能将超过平衡树索引(B-tree index),当此列与其他列作为组合条件时效果尤为明显。

平衡树索引(B-tree index)适用于高基数的数据,即数据的可能值很多,例如CUSTOMER_NAME 或 PHONE_NUMBER 列。在有些情况下,平衡树索引所需的存储空间可能比被索引数据还要大。如果使用得当,位图索引将远远小于同等情况下的平衡树索引。

对于自定义查询(ad hoc query)或相似的应用,使用位图索引(bitmap index)能够显著地提高查询性能。查询的 WHERE 子句中的 AND 和 OR 条件直接对位图(bitmap)进行布尔运算(Boolean operation)得到一个位图结果集(resulting bitmap),而无需将所有的位图转换为 ROWID。如果布尔操作后的结果集较小,那么查询就能够迅速得到结果,而无需进行全表扫描(full table scan)。

5.8.11.3 位图索引和空值

与其他大多数索引不同,位图索引(bitmap index)可以包含键值(key value)为 NULL 的行。将键值为空的行进行索引对有些 SQL 语句是有用处的,例如包含 COUNT 聚合函数的查询。

5.8.11.4 分区表上的位图索引

用户可以在分区表(partitioned table)上创建位图索引(bitmap index)。唯一的限制是位图索引对分区表来说必须是本地的(local),而不能是全局索引(global index)。只有非分区表才能使用全局位图索引。

5.8.12 位图连接索引

除了建立在单个表之上的位图索引(bitmap index),用户还可以创建位图连接索引(bitmap join index),此种索引是为了连接(join)两个或多个数据表而建的。位图连接索引(bitmap join index)可以预先将有连接关系的数据进行保存,且所需的存储空间较小。对于一个表的某列的每个值,位图连接索引为其保存其他表中与此值有连接关系的数据行的 rowid。在数据仓库环境中,连接关系通常是维表(dimension table)中的主键(primary key)与事实表(fact table)中的外键(foreign key)进行等值内连接(equi-inner join)。

物化连接视图(materialized join view)也是一种预先将连接物化的方法,但与之相比位图连接索引(bitmap join index)所需的存储空间更少。因为物化连接视图不会压缩事实表(fact table)中的 rowid。