外文翻译:索引原文来源:Thomas Kyte.Expert Oracle Database Architecture .2nd Edition.译文正文:什么情况下使用B*树索引?我并不盲目地相信“法则”(任何法则都有例外),对于什么时候该用B*索引,我没有经验可以告诉你。
为了证明为什么这个方面我无法提供任何经验,下面给出两种等效作法:•使用B*树索引,如果你想通过索引的方式去获得表中所占比例很小的那些行。
•使用B *树索引,如果你要处理的表和索引许多可以代替表中使用的行。
这些规则似乎提供相互矛盾的意见,但在现实中,他们不是这样的,他们只是涉及两个极为不同的情况。
有两种方式使用上述意见给予索引:•作为获取表中某些行的手段。
你将读取索引去获得表中的某一行。
在这里你想获得表中所占比例很小的行。
•作为获取查询结果的手段。
这个索引包含足够信息来回复整个查询,我们将不用去查询全表。
这个索引将作为该表的一个瘦版本。
还有其他方式—例如,我们使用索引去检索表的所有行,包括那些没有建索引的列。
这似乎违背了刚提出的两个规则。
这种方式获得将是一个真正的交互式应用程序。
该应用中,其中你将获取其中的某些行,并展示它们,等等。
你想获取的是针对初始响应时间的查询优化,而不是针对整个查询吞吐量的。
在第一种情况(也就是你想通过索引获得表中一小部分的行)预示着如果你有一个表T (使用与早些时候使用过的相一致的表T),然后你获得一个像这样查询的执行计划:ops$tkyte%ORA11GR2> set autotrace traceonly explainops$tkyte%ORA11GR2> select owner, status2 from t3 where owner = USER;Execution Plan----------------------------------------------------------Plan hash value: 1049179052------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes |------------------------------------------------------------------| 0 | SELECT STATEMENT | | 2120 | 23320 || 1 | TABLE ACCESS BY INDEX ROWID |T | 2120 | 23320 || *2 | INDEX RANGE SCAN | DESC_T_IDX | 8 | |------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------2 - access(SYS_OP_DESCEND("OWNER")=SYS_OP_DESCEND(USER@!))filter(SYS_OP_UNDESCEND(SYS_OP_DESCEND("OWNER"))=USER@!)你应该访问到该表的一小部分。
这个问题在这里看是INDEX (RANGE SCAN) 紧跟在TABLE ACCESS BY INDEX ROWID之后。
这也意味着Oracle先读取索引,然后获取索引项。
该索引项将执行一个数据库块读(逻辑或者物理的I/O)去获取行数据。
如果你想通过索引去访问数据表T中的大部分数据,这不是最高效的方式(我们将很快定义什么是大部分的数据)。
第二种情况,(也就是你想通过索引去代替表),你将通过索引去处理100%(事实上可以是任何比例)的行。
也许你想通过索引索引去获得一个缩小版的表。
接下来的查询证明了这种方式:ops$tkyte%ORA11GR2> select count(*)2 from t3 where owner = user;Execution Plan----------------------------------------------------------Plan hash value: 293504097---------------------------------------------------------------------------|Id | Operation. | Name | Rows | Bytes .| Cost (%CPU)| Time |---------------------------------------------------------------------------| 0 | SELECT STA TEMENT | | 1 | 6 | 17 (0) | 00:00:01 || 1 | SORT AGGREGAT E | | 1 | 6 | .. | .|| * 2 | INDEX RANGE SCAN | T_IDX | 2 120 | 12720 | 17 (0) | 00: 00: 01 |---------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------2 - access("OWNER"=USER@!)这里,仅仅是使用索引去作为查询的返回集-现在再也不在乎我们只通过索引的方式,想访问多少比例的行。
从执行计划中可以看到,查询语句从未访问过表,仅仅扫描索引结构本身。
理解两种概念的区别很重要。
当执行TABLE ACCESS BY ROWID操作时,我们必须确保仅访问表中一小部分的块,也就相当于仅访问一小部分的行或者是尽量块地获取第一的数据。
(最终的用户将会为了这几行数据等得不耐烦的)。
如果想通过访问比较高比例的行(所占比例高于20%),使用B*索引的话,它将花费比全表扫描更多的时间。
使用第二种查询方式,那些在索引中可以找到所需结果的,情况就完全不同了。
我们读取索引块,然后拾取其中的很多行进行处理,如此继续下一个索引块,从不访问表。
某些情况下,还可以在索引上执行一个快速全面扫描。
快速全面扫描是指,数据库不按特定的顺序读取索引块,只是开始读取它们。
这里不再是将索引只当一个索引,此时更像是一个表。
如果采用全表扫描,将不会按索引项来顺序获取行。
一般来讲,B*树索引将会被放在查询时频繁使用的列上。
而且我们希望从表中只返回少量的数据或者最终用户的请求想立即得到反馈。
在一个瘦表(也就是一个含有很少的列或者列很小)中,这个比例可能很小。
一个查询,使用该索引应该可以在表中取回约2% ~ 3%或更少的行。
在一个胖表(也就是含有很多列或者列很宽)中,这个比例将一直上升到该表的20%~25%。
以上建议并不对每个人都有作用。
这个比例并不直观,但很精确。
索引根据索引键进行排序存储。
索引会按键的有序顺序进行访问。
索引指向的块都随机存储在堆中。
因此,我们通过索引访问表时,会执行大类分散、随机的I/O。
这里的“分散”是指,索引会告诉我们读取块1,然后是块1000,块205,块1,块1032,块1等等。
它们不会要求我们按照块1,块2然后块3的方式。
我们将以一种非常随意的方式读取和重新读取块,这种块I/O可能非常慢。
让我们看一下简化版的例子,假设我们通过索引读取一个瘦表,而且要读取表中的20%的行。
若这个表中有100000行,这个表得20%就是20000行。
如果行大小约为80个字节,在一个块大小为8KB的数据库中,我们将在每个块中获得100行数据。
这也就意味着这个表有1000个块。
了解这些,计算起来就很容易了。
我们想通过索引去读取2000行,这也就意味着几乎相当于20000次的TABLE ACCESS BY ROWID 操作。
这将导致执行这个操作要处理20000个表块。
不过,这个表总共才只有1000块。
我们将对表的每个块要执行读和处理20次。
即时把行的大小提高到一个数量级,达到每行800字节,这样每块有10行,那样这个表现在有10000块。
要通过索引20000行,仍要求我们把每一块平均读取2次。
在这种情况下,全表扫描就比使用索引高效得多。
因为每个块只会命中一次。
如果把查询使用这个索引来访问数据,效率都不会高,除非对应800字节的行,平均只访问表中不到5%的数据(这样一来,我们访问的大概为5000块),如果是80字节的行,则访问的数据应当只占更小的百分比(大约0.5%或更少)。
什么情况下使用位图索引?位图索引是最适合于低相异基数数据的情形(也就是说,与整个数据集得基数相比,这个数据只有很少几个不同的值)。
对此作出量化是不太可能的——换句话说,就是很难定义这个低相异基数数据有到底多么不同。
在一个有几千条记录的数据集中,2就是一个低相异基数,但是在一个只有两行记录的数据表中,2就不再是低相异基数了。
而在一个上千万或者上亿条记录的表中,甚至100,000都能作为一个低相异基数。
所以,多大才算是低相异基数,这要相对于结果集得大小来说。
这里是指行集中不同项的个数除以行数应该是一个很小的数(接近于0)。
例如,GENDER列可能取值为M、F和NULL。
如果一个表中有20,000条员工记录,那么你将会发现3/20,000=0.00015。
同样地,10,000,000中100,000个不同值得比例为0.01,——同样,值很小。
这些列就可以建立位图索引。
他们可能不合适建立B*树索引,因为每个值可能会获取表中的大量数据。
如同前面所述,B*数索引一般来讲是选择性的。
位图索引不带有选择性的——相反,一般是“没有选择性”的。
位图索引在有很多即时查询的时候极其有用,尤其是在查询涉及很多列或者会生成诸如COUNT之类的聚会。
例如,假设有一个含有GENDER,LOCATION,和AGE_GROUP三个字段的大表。
在这个表中,GENDER的值为M或者F,LOCATION可以选取1到50之间的值,AGE_GROUP为代表18岁及以下,19-25,26-30,31-40,和40岁及以上的代码。