1、B*-Tree Defined
B*-树索引是数据库和文件系统中重要的访问路径结构,其关键特性是每个可能的搜索路径都具有相同的长度。B*-树由Bayer和McCreight于1972年首次描述,是一种维护有序数据集并允许对数据进行高效查找、删除、插入和浏览操作的数据结构。B*-树由包含键和链接B*-树节点的指针的节点记录组成,键在每个节点内保持有序排列。指针列表有效地穿插在键之间,指示如果当前节点中没有该键,则应在哪里搜索该键。
B*-树结构具有以下优点:
- 树的所有叶块都位于相同的深度,因此从索引中的任何位置检索任何记录所需的时间大致相同。
- B*-树索引自动保持平衡。
- B*-树的所有块平均填充三分之四。
- B*-树为各种查询(包括精确匹配和范围搜索)提供了优秀的检索性能。
- 插入、更新和删除操作高效,维持键顺序以快速检索。
2、Balancing of B*-Tree
B*-树的层级总是自底向上增长。B*-树从平衡状态开始(一个节点);所有改变其高度的操作都会保持其平衡状态。
具体地,插入操作包括以下步骤:
- 搜索算法定位到索引集最低层的节点N,其中包含一个键K逻辑上应该属于的位置。如果节点N有空闲空间,键K将被插入到节点N中,然后过程终止。否则:
- 节点N被分裂成两个节点,然后对键进行排序。在最坏的情况下,分裂会一直发生到树的顶部;必须分裂根节点(父节点),树的高度会增加一层。条目按ROWID顺序插入叶块。因此,所有块依次被扫描,直到找到一个具有大于新行的ROWID的条目,然后将该行插入到该块中。
删除操作是插入操作的相反过程。当必须删除索引条目时,行将从索引叶块中删除,并释放索引叶块内的空间以供具有适当键范围的进一步插入使用。即使叶块只有一个条目,它仍然是树的一部分;因此,只有定位上属于它的条目才能被容纳。
更新操作通过删除旧值并插入新值来处理。
3、B*-Tree Structure
- 分支块(Branch blocks)保存<分隔键, DBA(数据库地址)>对,用于指导B*-树搜索到达一个叶块条目。
- 叶块(Leaf blocks)保存<索引键, ROWID>对。
B*-树的结构初步是这样的:最初,每个索引树只有一个层级。如果表中的数据非常少,可能只有一个索引块。在这种情况下,叶块和分支块是相同的。随着数据量的增长,层级增加,然后出现分支块和叶块,它们之间存在父子关系。分隔键只是决定每个块存储哪些值的索引键。
4、Index Block Structure
索引块的布局包括:头部信息、行片段信息。
- 块头部(Block header)包含缓存和事务层信息。
- 通用头部(Common header)用于分支和叶子块,包含了行片段信息。
索引块结构的关键组成部分包括: - 索引块级别(kdxcolev),0表示叶块,大于0表示分支块。
- 事务持有块锁的ITL(kdxcolok),用于指示当前有事务正在操作该块,以防其他事务尝试更新该块。
- 索引块中键的数量(kdxconco),0表示该块不在B*-树中。
- 每次块被分裂或删除时,计数器(kdxcosdc)会增加。
- 索引块中行的数量(kdxconro)。
- 可用空间的开始和结束偏移量(kdxcofbo和kdxcofeo)。
- 块中可用的空间量(kdxcoavs),不包括已提交分裂空洞的空间。
此外,还介绍了索引块操作的不同类型,如插入、删除、分裂、合并等操作的标识符,以及用于标识索引块类型的标志位。
/* The operation code describes the operation being performed by
the service transaction which currently holds the itl block lock.
If kdxcolok is non-zero then service transaction is probably not
committed. If kdxgopc(ixhdr) is zero, then the service transaction
is committed but */
#define KDXONOOP 0 /* no operation in progress */
/* Branch block operations */
#define KDXOINSROW 1 /* insert into slot kdxbrsno in branch block */
#define KDXODELROW 2 /* delete slot kdxbrsno in branch block */
#define KDXOSPLIT 3 /* post-split (block already split in 2 halves)
*/
/* Branch and leaf block operations */
#define KDXOPRESPL 5 /* pre-split (split about to be done); */
/* for leaf blocks, opcode cleared
as part of commit. */
#define KDXODELBLK 6 /* delete block from b-tree. */
#define KDXONEW 7 /* init new block for b-tree during split */
/* Leaf block operations */
#define KDXOPREV 9 /* set previous link (kdxleprv) */
#define KDXONEXT 10 /* set next link (kdxlenxt) */
#define KDXOPURGE 11 /* purge committed split-holes from block; */
/* is cleared as part of commit. */
#define KDXOCANCEL 12 /* KDXOPRESPL operation cancelled by rollback*/
#define KDXOPREMG 13 /* pre-merge index blocks */
#define KDXONONE 0x00 /* V7 btree or branch block */
#define KDXOIOT 0x10 /* iot btree */
#define KDXOKCMP 0x20 /* key compression */
#define KDXONTCOL 0x40 /* iot number nested key cols > 0 */
#define KDXOISV8 0x80 /* V8 block */
#define KDXOMASK 0x0F /* mask for opcode bits */
#define KDXOFLAG 0xF0 /* mask for flag bits */
#define KDXOIOTFLAG 0x70 /* mask for iot flag bits */
ub1 kdxconco; /* # columns in key; 0 => block not in b-tree */
kdxsdc kdxcosdc; /* increment each time block is Split or Deleted */
kd_sno kdxconro; /* # rows in the row index */
kd_off kdxcofbo; /* free space beginning offset */
kd_off kdxcofeo; /* free space ending offset (first used byte) */
kd_off kdxcoavs; /* available space (committed) in the block */
/* kdxcoavs does not include space for committed split holes.
Space for locked rows marked deleted is recorded in the itl
free space credit fields for the transactions holding the locks.*/
5、Branch Block Dump
kdxcolev表示块的级别,这里为1,表明这是一个分支块。kdxcolok和kdxcoopc提供了关于块锁和操作代码的信息。例如,kdxcoopc 0x80表示操作代码为0,iot标志为---,已转换为Y。kdxconco表示键的数量,在这个例子中为1。kdxcosdc、kdxconro、kdxcofbo、kdxcofeo和kdxcoavs提供了关于块的分裂、行索引数量、可用空间的开始和结束偏移量以及块中可用空间的信息。kdxbrlmc和kdxbrsno提供了关于分支块左侧最子块的指针和最后一次服务事务修改的槽位信息。
分支块转储还包括数据部分,显示了每一行(或条目)的详细信息,包括数据库地址(DBA)、分隔键的长度和数据。例如,row#0[1904] dba: 4203362=0x402362显示了第一行的数据库地址和分隔键的值。
此外,还介绍了分支块特定的结构kdxbr,以及与分支块大小相关的信息kdxbrbksz。
6、Type of Index Leaf Block
索引叶块的两种类型:非唯一索引叶块和唯一键索引叶块,以及它们在存储ROWID数据时的区别。
- 非唯一索引叶块:在这种类型的叶块中,ROWID被视为索引键的另一个列。每个列都有长度和数据对,而且
kdxledsz(在行头中指示ROWID数据大小的字段)的值为零。 - 唯一索引叶块:对于唯一键索引叶块,ROWID存储在行头中,
kdxledsz有一个非零的值。这表明ROWID数据直接包含在行头中,而不是作为索引键的一部分。
此外,还提到了叶块头部的结构,包括公共索引块头部信息、由解锁的分裂孔占据的空间、块中标记为已删除的孔的数量、下一个叶块的地址等信息。
7、Non-Unique Leaf Block
在非唯一索引叶块中,ROWID被视作索引键的另一个列,每个列都有长度和数据对,而且kdxledsz(在行头中指示ROWID数据大小的字段)的值为零。
关键信息包括:
kdxcolev为0,表示这是一个叶块。kdxcolok表示持有块锁的状态。kdxcoopc提供操作代码信息,0x80表示特定的操作或状态。kdxconco表示键的数量,在非唯一叶块的例子中为2。kdxcofbo和kdxcofeo分别表示可用空间的开始和结束偏移量。kdxcoavs表示块中可用的空间量。kdxlespl和kdxlende分别表示由解锁的分裂孔占据的空间和块中标记为已删除的孔的数量。kdxlenxt和kdxleprv分别表示下一个和前一个叶块的地址。kdxledsz为0,表明这是一个非唯一索引叶块。
数据部分展示了每一行(或条目)的详细信息,包括列的长度和数据。例如,row#0[1895]和row#1[1882]展示了两个具有不同ROWID但相同索引键值的条目,这正是非唯一索引叶块允许的情况。
8、Diagnostics
几种用于诊断和分析Oracle数据库中的索引问题的工具和方法。这些工具和方法包括:
- Dbverify:这是一个外部命令行工具,用于验证数据库文件的完整性。它可以帮助识别数据文件中的块损坏问题。
- Analyze:这是SQL命令,用于收集表和索引的统计信息,也可以用来验证结构的完整性。通过执行
ANALYZE TABLE ... VALIDATE STRUCTURE和ANALYZE INDEX ... VALIDATE STRUCTURE命令,可以检测到表和索引中的潜在问题。 - Dump of a tree structure:这涉及到生成B*-树(索引结构)的转储,以分析和理解索引的物理结构。这对于深入理解索引的布局和潜在的问题非常有用。
- Index-related trace events:Oracle提供了多种跟踪事件来帮助诊断索引相关的问题。这些跟踪事件可以在会话或实例级别启用,以收集关于索引操作的详细信息,这对于诊断问题和优化索引性能非常有帮助。
9、Obtaining and Interpreting Index Trees
如何获取和解读索引树(B*-树)的结构,这对于理解和优化Oracle数据库中的索引非常重要。具体步骤如下:
- 获取索引树:可以通过执行ALTER SESSION命令并使用特定的事件来获取索引树的转储,例如:
其中ALTER SESSION SET EVENTS immediate trace name TREEDUMP level <nn>';<nn>是索引的OBJECT_ID。这个命令会生成一个包含索引树结构的跟踪文件。 - 解读索引树:转储文件中的内容会显示索引树的层级结构,包括分支节点和叶节点。每个节点会显示如下信息:
-
节点类型(分支或叶)
-
节点地址
-
节点中的行数(nrow)
-
实际行数(rrow,如果有的话)
-
节点的层级(level)
通过这些信息,可以了解索引的深度和分布,以及数据在索引中是如何组织的。
-
- 示例:转储文件中的内容可能类似于以下格式,展示了从根节点到叶节点的结构:
这显示了索引树的不同层和节点,以及每个节点中的行数。branch: 0x800afe 8391422 (0: nrow: 2, level: 2) leaf: 0x800aff 8391423 (-1: nrow: 103 rrow: 85) leaf: 0x800b00 8391424 (0: nrow: 103 rrow: 97) ...
10、Index-Related Trace Events
Oracle数据库中用于诊断和分析索引相关操作的跟踪事件。这些跟踪事件可以帮助数据库管理员深入理解索引操作的内部工作机制,从而对性能问题进行故障排除或进行优化。以下是一些关键的索引相关跟踪事件:
- 10224 “index block split/delete trace”:这个事件用于跟踪索引块分裂或删除操作的详细信息,有助于理解索引结构变化。
- 10607 “trace index rowid partition scan”:该事件用于跟踪索引ROWID分区扫描操作,有助于分析分区表上的索引访问模式。
- 10233 “skip corrupted blocks on index operations”:此事件允许在索引范围扫描操作中跳过损坏的块,有助于在处理损坏数据块时维持数据库的可访问性。
- 10608 “trace create bitmap index”:用于跟踪位图索引创建过程的详细信息,有助于优化位图索引的创建和维护。
- 10606 “trace parallel create index”:该事件用于跟踪并行创建索引的操作,有助于分析和优化大表上索引创建的性能。
- 10211 “index block checking”:这个事件用于在Oracle8i及更早版本中进行索引块检查,虽然在Oracle9i及更高版本中被DB_BLOCK_CHECKING参数取代,但对于旧版本数据库仍然有用。
这些跟踪事件提供了一种强大的机制,通过生成详细的诊断信息来帮助理解和解决索引相关的问题。
11、ORA-8102: Index Corruption
ORA-8102错误,这是Oracle数据库中索引损坏的一个常见错误码。错误信息为“index key not found, obj# %s, dba %s (%s)”,意味着在索引中找不到与表中存储的值相匹配的键,表明发生了某种形式的索引或表损坏。
原因和解决方法包括:
- 原因:ORA-8102错误可能由多种原因引起,包括内存损坏、硬盘损坏或控制器问题、系统崩溃等,导致索引与表数据之间的不一致。
- 解决方法:
- 重建索引:通常,通过删除并重新创建索引可以解决这个问题。这是因为重新创建索引会重新处理表中的数据,生成一个新的、未损坏的索引。
- 数据恢复:如果错误经常发生,建议从备份和归档日志文件中恢复数据库,以确保数据的完整性。
- 诊断和分析:如果问题持续存在,可能需要进行更深入的诊断。Oracle建议使用
ANALYZE TABLE ... VALIDATE STRUCTURE CASCADE;命令来分析表和索引的结构,这可以帮助识别键值不一致和块损坏等问题。
ORA-8102错误指示了索引损坏的问题,需要及时解决以避免数据访问问题或性能下降。通过上述方法,数据库管理员可以诊断和解决索引损坏的问题,确保数据库的稳定运行。
12、ORA-8102: Detecting Index Corruption
如何检测Oracle数据库中的索引损坏。当遇到ORA-8102错误时,表示索引中的键与表中的值不匹配,这可能是由于索引或表的损坏。为了诊断和解决这个问题,以下步骤可以帮助检测索引损坏:
- 转储索引块:首先,需要转储有问题的索引块。这可以通过对索引执行块转储操作来完成,以获取索引块的内部结构和内容。
- 使用ROWID选择记录:尝试使用ROWID直接从表中选择记录。这个操作可以帮助确认表中的数据是否完好无损。可以使用以下SQL命令:
这个命令会返回表中键列的转储信息,可以用来与索引块转储中的值进行比较。SELECT dump(keycol1), dump(keycol2)... FROM ... WHERE rowid=xxxxxx; - 比较索引和表中的值:通过比较表中的值和索引块转储中的值,可以确定不匹配的地方。这有助于识别损坏的索引条目。
- 采取适当的行动:一旦发现了损坏的索引条目,可以根据情况采取适当的行动。对于索引损坏,通常的解决方案是删除并重新创建索引。对于表行损坏,可能需要更新行以纠正错误的值,然后重新创建索引。
- 设置跟踪事件:如果分析表结构有效,可以在
init.ora文件中设置事件"8102 trace name errorstack forever",然后检查生成的跟踪文件以获取更多诊断信息。
检测和解决索引损坏是确保数据库健康和性能的关键步骤。通过上述方法,数据库管理员可以有效地识别和修复索引损坏问题。




