`

(转)深入研究B树索引(四)续

阅读更多

4.2  B 树索引的对于删除( DELETE) 管理

       上面介绍了有关插入键值时索引的管理机制,那么对于删除键值时会怎么样呢?

在介绍删除索引键值的机制之前,先介绍与索引相关的一个比较重要的视图: index_stats 。该视图显示了

大量索引内部的信息,该视图正常情况下没有数据,只有在运行了下面的命令以后才会被填充数据,而且该视图中只能存放一条与分析过的索引相关的记录,不会有第二条记录。同时,也只有运行了该命令的 session 才能够看到该视图里的数据,其他 session 不能看到其中的数据。

Sql代码  收藏代码
  1. analyze  index  INDEX_NAME validate structure;  
 

       不过要注意一点,就是该命令有一个坏处,就是在运行过程中,会锁定整个表,从而阻塞其他 session 对表进行插入、更新和删除等操作。这是因为该命令的主要目的并不是用来填充 index_stats 视图的,其主要作用在于校验索引中的每个有效的索引条目都对应到表里的一行,同时表里的每一行数据在索引中都存在一个对应的索引条目。为了完成该目的,所以在运行过程中要锁定整个表,同时对于很大的表来说,运行该命令需要耗费非常多的时间。

在视图 index_stats 中, height 表示 B 树索引的高度; blocks 表示分配了的索引块数,包括还没有被使用的; pct_used 表示当前索引中被使用了的空间的百分比。其值是通过该视图中的 (used_space/btree_space)*100 计算而来。 used_space 表示已经使用的空间,而 btree_space 表示索引所占的总空间; del_lf_rows 表示被删除的记录行数(表里的数据被删除并不会立即将其对应于索引里的索引条目清除出索引块,我们后面会说到); del_lf_rows_len 表示被删除的记录所占的总空间; lf_rows 表示索引中包含的总记录行数,包括已经被删除的记录行数。这样的话,索引中未被删除的记录行数就是 lf_rows-del_lf_rows 。同时我们可以计算未被删除的记录所对应的索引条目(也就是有效索引条目)所占用的空间为 ((used_space – del_lf_rows_len) / btree_space) * 100

然后,我们还是接着上个例子(最后插入了 12*2 的例子)来测试一下。这时我们已经知道,该例中的索引具有两个叶子节点,一个叶子节点(块号为 419 )包含 10 12 14 16 18 20 22 24 2a ,而另一个叶子节点(块号为 420 )包含 4a 6a 8a 。我们插入 41 42 43 44 45 46 47 48 8 条记录,这时可以知道这 8 条记录所对应的索引条目将会进入索引块 420 中,从而该块 420 被充满。

Sql代码  收藏代码
  1. SQL>  begin   
  2.   
  3.  2    for  i  in  1..8 loop  
  4.   
  5.  3        insert   into  index_test  values  (rpad( '4' ||to_char(i),150, 'a' ));  
  6.   
  7.  4    end  loop;  
  8.   
  9.  5 end ;  
  10.   
  11.  6 /  
 

我们先分析索引从而填充 index_stats 视图。

Sql代码  收藏代码
  1. SQL> analyze  index  idx_test validate structure;  
  2.   
  3. SQL> select  LF_ROWS,DEL_LF_ROWS,DEL_LF_ROWS_LEN,USED_SPACE,BTREE_SPACE  from  index_stats;  
  4.   
  5.      LF_ROWS DEL_LF_ROWS DEL_LF_ROWS_LEN USED_SPACE BTREE_SPACE  
  6.   
  7. ---------- ----------- --------------- ---------- -----------   
  8.   
  9.        20          0              0      3269       5600  
 

       从上面视图可以看到,当前索引共 20 条记录,没有被删除的记录,共使用了 3269 个字节。

然后我们删除位于索引块 419 里的索引条目,包括 10 12 14 16 4 条记录。

Sql代码  收藏代码
  1. SQL>  delete  index_test  where  substr(id,1,2)  in ( '10' , '12' , '14' , '16' );  
  2.   
  3. SQL> commit ;  
  4.   
  5. SQL> alter  system dump datafile 7 block 419;  
 

       打开转储出来的文件可以发现如下的内容(我们节选了部分关键内容)。可以发现, kdxconro 3 ,说明该索引节点里还有 9 个索引条目。所以说, 虽然表里的数据被删除了,但是对应的索引条目并没有被删除,只是在各个索引条目上( row# 一行中的 flag D )做了一个 D 的标记,表示该索引条目被 delete 了。

Sql代码  收藏代码
  1. kdxconro 9  
  2.   
  3. row#0[443] flag: ---D-, lock: 2   
  4.   
  5. row#1[604] flag: ---D-, lock: 2   
  6.   
  7. row#2[765] flag: ---D-, lock: 2   
  8.   
  9. row#3[926] flag: ---D-, lock: 2   
 

       然后,我们再以树状结构转储索引,打开树状转储跟踪文件可以看到如下内容。可以知道,块 419 里包含 9 个索引条目( nrow 9 ),而有效索引条目只有 5 个( rrow 5 ),那么被删除了的索引条目就是 4 个( 9 5 )。

Sql代码  收藏代码
  1. SQL>  alter  session  set  events  'immediate trace name treedump level 7390' ;  
  2.   
  3. ----- begin tree dump   
  4.   
  5. branch: 0x1c001a2 29360546 (0: nrow: 2, level : 1)  
  6.   
  7.   leaf: 0x1c001a3 29360547 (-1: nrow: 9 rrow: 5)  
  8.   
  9.   leaf: 0x1c001a4 29360548 (0: nrow: 11 rrow: 11)  
  10.   
  11. ----- end tree dump   
 

       这时,我们再次分析索引,填充 index_stats 视图。

Sql代码  收藏代码
  1. SQL> analyze  index  idx_test validate structure;  
  2.   
  3. SQL> select  LF_ROWS,DEL_LF_ROWS,DEL_LF_ROWS_LEN,USED_SPACE,BTREE_SPACE  from  index_stats;  
  4.   
  5.      LF_ROWS DEL_LF_ROWS DEL_LF_ROWS_LEN USED_SPACE BTREE_SPACE  
  6.   
  7. ---------- ----------- --------------- ---------- -----------   
  8.   
  9.        20          4            652      3269       5600  
 

       对照删除之前视图里的信息,很明显看到,当前索引仍然为 20 条记录,但是其中有 4 条为删除的,但是索引所使用的空间并没有释放被删除记录所占用的 652 个字节,仍然为删除之前的 3269 个字节。这也与转储出来的索引块的信息一致。

       接下来,我们测试这个时候插入一条记录时,索引会怎么变化。分三种情况进行插入:第一种是插入一个属于原来被删除键值范围内的值,比如 13 ,观察其会如何进入包含设置了删除标记的索引块;第二种是插入原来被删除的键值中的一个,比如 16 ,观察其是否能够重新使用原来的索引条目;第三种是插入一个完全不属于该表中已有记录的范围的值,比如 rpad('M',150,'M') ,观察其对块 419 以及 420 会产生什么影响。

       我们测试第一种情况:

Sql代码  收藏代码
  1. SQL>  insert   into  index_test  values  (rpad(to_char(13),150, 'a' ));  
  2.   
  3. SQL> alter  system dump datafile 7 block 419;  
 

       打开跟踪文件以后会发现 419 块里的内容发生了变化,如下所示。我们可以发现一个很有趣的现象,从 kdxconro 6 说明插入了键值 13 以后,导致原来四个被标记为删除的索引条目都被清除出了索引块。同时,我们也确实发现原来标记为 D 的四个索引条目都消失了。

Sql代码  收藏代码
  1. ……  
  2.   
  3. kdxconro 6  
  4.   
  5. ……  
  6.   
  7. kdxlende 0  
  8.   
  9. ……  
  10.   
  11. row#0[121] flag: -----, lock: 2   被插入13   
  12.   
  13. col 0; len 150; (150):  
  14.   
  15.  31 33 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61  
  16.   
  17. ……  
 

我们分析索引,看看 index_stats 视图会如何变化。

Sql代码  收藏代码
  1. SQL> analyze  index  idx_test validate structure;  
  2.   
  3. SQL> select  LF_ROWS,DEL_LF_ROWS,DEL_LF_ROWS_LEN,USED_SPACE,BTREE_SPACE  from  index_stats;  
  4.   
  5.   LF_ROWS DEL_LF_ROWS DEL_LF_ROWS_LEN USED_SPACE BTREE_SPACE  
  6.   
  7. ---------- ----------- --------------- ---------- -----------   
  8.   
  9.        17          0              0      2780       5600  
 

       很明显,原来的 del_lf_rows 4 变为了 0 ,同时 used_space 也从原来的 3269 变成了 2780 。表示原来被删除的索引条目所占用的空间已经释放了。

我们继续测试第二种情况:

Sql代码  收藏代码
  1. SQL>  insert   into  index_test  values  (rpad(to_char(8*2),150, 'a' ));  
  2.   
  3. SQL> alter  system dump datafile 7 block 419;  
 

       打开跟踪文件以后,发现对于插入已经被标记为删除的记录来说,其过程与插入属于该索引块索引范围的键值的过程没有区别。甚至你会发现,被插入的 16 的键值所处的位置与插入的 13 的键值所在的位置完全一样( row#0[121] 里的 121 表示在索引块中的位置)。也就是说, oracle 并没有重用原来为 16 的键值,而是直接将所有标记为 D 的索引条目清除出索引块,然后插入新的键值为 16 的索引条目。

       对于第三种情况,我们已经可以根据前面有关第一、第二种情况做出预测,由于 420 块已经被充满,同时所插入的键值是整个表里的最大值,因此也不会因此 420 号块的分裂,而是直接获取一个新的索引块来存放该键值。但是 419 号块里标记为 D 的索引条目是否能被清除出索引块呢?

Sql代码  收藏代码
  1. SQL>  insert   into  index_test  values  (rpad( 'M' ,150, 'M' ));  
  2.   
  3. SQL> alter  system dump datafile 7 block 419;  
  4.   
  5. SQL> alter  system dump datafile 7 block 420;  
  6.   
  7. SQL> alter  system dump datafile 7 block 421;  
 

       打开跟踪文件,可以清楚的看到, 419 号块里的标记为 D 4 各索引条目仍然保留在索引块里,同时 420 号块里的内容没有任何变化,而 421 号块里则存放了新的键值: rpad('M',150,'M')

我们看看 index_stats 视图会如何变化。其结果也符合我们从转储文件中所看到的内容。

Sql代码  收藏代码
  1. SQL> analyze  index  idx_test validate structure;  
  2.   
  3. SQL> select  LF_ROWS,DEL_LF_ROWS,DEL_LF_ROWS_LEN,USED_SPACE,BTREE_SPACE  from  index_stats;  
  4.   
  5.   LF_ROWS DEL_LF_ROWS DEL_LF_ROWS_LEN USED_SPACE BTREE_SPACE  
  6.   
  7. ---------- ----------- --------------- ---------- -----------   
  8.   
  9.        21          4            652      3441       7456  
 

       既然当插入 rpad('M',150,'M') 时对 419 号块没有任何影响,不会将标记为 D 的索引条目移出索引块。那么如果我们事先将 419 号索引块中所有的索引条目都标记为 D ,也就是说删除 419 号索引块中索引条目所对应的记录,然后再次插入 rpad('M',150,'M') 时会发生什么?通过测试,我们可以发现,再次插入一个最大值以后,该最大值会进入块 421 里,但是块 419 里的索引条目则会被全部清除,变成了一个空的索引数据块。这也就是我们通常所说的,当索引块里的索引条目全部被设置为 D (删除)标记时,再次插入任何一个索引键值都会引起该索引块里的内容被清除。

       最后,我们来测试一下,当索引块里的索引条目全部被设置为 D (删除)标记以后,再次插入新的键值时会如何重用这些索引块。我们先创建一个测试表,并插入 10000 条记录。

Sql代码  收藏代码
  1. SQL>  create   table  delete_test(id number);  
  2.   
  3. SQL> begin   
  4.   
  5.  2    for  i  in  1..10000 loop  
  6.   
  7.  3        insert   into  delete_test  values  (i);  
  8.   
  9.  4    end  loop;  
  10.   
  11.  5    commit ;  
  12.   
  13.  6 end ;  
  14.   
  15.  7 /  
  16.   
  17. SQL> create   index  idx_delete_test  on  delete_test(id);  
  18.   
  19. SQL> analyze index  idx_delete_test validate structure;  
  20.   
  21. SQL> select  LF_ROWS,LF_BLKS,DEL_LF_ROWS,USED_SPACE,BTREE_SPACE  from  index_stats;  
  22.   
  23.   LF_ROWS   LF_BLKS DEL_LF_ROWS USED_SPACE BTREE_SPACE  
  24.   
  25. ---------- ---------- ----------- ---------- -----------   
  26.   
  27.     10000        21          0    150021     176032  
 

       可以看到,该索引具有 21 个叶子节点。然后我们删除前 9990 条记录。从而使得 21 个叶子节点中只有最后一个叶子节点具有有效索引条目,前 20 个叶子节点里的索引条目全都标记为 D (删除)标记。

Sql代码  收藏代码
  1. SQL>  delete  delete_test  where  id >= 1  and  id <= 9990;  
  2.   
  3. SQL> commit ;  
  4.   
  5. SQL> analyze index  idx_delete_test validate structure;  
  6.   
  7. SQL> select  LF_ROWS,LF_BLKS,DEL_LF_ROWS,USED_SPACE,BTREE_SPACE  from  index_stats;  
  8.   
  9.   LF_ROWS   LF_BLKS DEL_LF_ROWS USED_SPACE BTREE_SPACE  
  10.   
  11. ---------- ---------- ----------- ---------- -----------   
  12.   
  13.     10000        21       9990   150021     176032  
 

       最后,我们插入从 20000 开始到 30000 结束,共 10000 条与被删除记录完全不重叠的记录。

Sql代码  收藏代码
  1. SQL>  begin   
  2.   
  3.  2    for  i  in  20000..30000 loop  
  4.   
  5.  3        insert   into  delete_test  values  (i);  
  6.   
  7.  4    end  loop;  
  8.   
  9.  5    commit ;  
  10.   
  11.  6 end ;  
  12.   
  13.  7 /  
  14.   
  15. SQL> analyze index  idx_delete_test validate structure;  
  16.   
  17. SQL> select  LF_ROWS,LF_BLKS,DEL_LF_ROWS,USED_SPACE,BTREE_SPACE  from  index_stats;  
  18.   
  19.   LF_ROWS   LF_BLKS DEL_LF_ROWS USED_SPACE BTREE_SPACE  
  20.   
  21. ---------- ---------- ----------- ---------- -----------   
  22.   
  23.     10011        21          0    160302     176032  
 

       很明显的看到,尽管被插入的记录不属于被删除的记录范围,但是只要索引块中所有的索引条目都被删除了(标记为 D ),该索引就变成可用索引块而能够被新的键值重新利用了。

       因此,根据上面我们所做的试验,可以对索引的删除情况总结如下:

1)  当删除表里的一条记录时,其对应于索引里的索引条目并不会被物理的删除,只是做了一个删除标记。

2)  当一个新的索引条目进入一个索引叶子节点的时候, oracle 会检查该叶子节点里是否存在被标记为删除的索引条目,如果存在,则会将所有具有删除标记的索引条目从该叶子节点里物理的删除。

3)  当一个新的索引条目进入索引时, oracle 会将当前所有被清空的叶子节点(该叶子节点中所有的索引条目都被设置为删除标记)收回,从而再次成为可用索引块。

尽管被删除的索引条目所占用的空间大部分情况下都能够被重用,但仍然存在一些情况可能导致索引空间

被浪费,并造成索引数据块很多但是索引条目很少的后果,这时该索引可以认为出现碎片。而导致索引出现碎片的情况主要包括:

1)  不合理的、较高的 PCTFREE 。很明显,这将导致索引块的可用空间减少。

2)  索引键值持续增加(比如采用 sequence 生 成序列号的键值),同时对索引键值按照顺序连续删除,这时可能导致索引碎片的发生。因为前面我们知道,某个索引块中删除了部分的索引条目,只有当有键值进 入该索引块时才能将空间收回。而持续增加的索引键值永远只会向插入排在前面的索引块中,因此这种索引里的空间几乎不能收回,而只有其所含的索引条目全部删 除时,该索引块才能被重新利用。

3)  经常被删除或更新的键值,以后几乎不再会被插入时,这种情况与上面的情况类似。

对于如何判断索引是否出现碎片,方法非常简单:直接运行 ANALYZE INDEX … VALIDATE STRUCTURE

命令,然后检查 index_stats 视图的 pct_used 字段,如果该字段过低(低于 50 %),则说明存在碎片。

4.3 B 树索引的对于更新( UPDATE )的管理

而对于值被更新对于索引条目的影响,则可以认为是删除和插入的组合。也就是将被更新的旧值对应的索

引条目设置为 D (删除)标记,同时将更新后的值按照顺序插入合适的索引块中。这里就不重复讨论了。


 

图4

图4

分享到:
评论

相关推荐

    【转】深入研究B树索引

    **深入研究B树索引** B树(B-tree)是一种自平衡的树数据结构,广泛应用于数据库和文件系统中,特别是在实现索引存储时。它能够保持数据有序,允许快速查找、插入和删除操作。本篇文章将深入探讨B树的核心概念、...

    深入研究B树索引.doc

    B树索引是一种高效的数据检索方法,广泛应用于数据库系统中,特别是关系型数据库管理系统。B树索引的主要目的是加速数据的查找、排序和更新操作,通过构建一种平衡的多路搜索树结构,使得数据访问更加高效。 B树...

    B+树索引 B+树索引

    B+树索引 B+树索引 B+树索引 B+树索引 B+树索引 B+树索引

    B树索引算法 VC实现

    **B树索引算法** B树(B-tree)是一种自平衡的树数据结构,它能够保持数据排序,常用于数据库和文件系统中。B树的主要特点是每个节点可以有多个子节点,这使得它在处理大量数据时能有效地进行查找、插入和删除操作...

    B树索引简单实验

    通过对B树索引简单实验的学习,我们不仅了解了B树索引的基本概念和创建方法,还深入了解了其查询过程和执行计划。这些知识对于数据库开发人员来说是非常重要的,能够帮助他们更好地理解和优化数据库查询性能。

    B树索引的研究

    Oracle 索引研究

    B+树索引实战.pdf

    B+树是一种广泛应用于数据库索引的平衡树数据结构,它通过维护数据的有序性,提高了数据检索的效率。在数据库系统中,B+树索引是实现快速查找、更新和删除操作的基础。本文将详细介绍B+树索引的原理及其在实际应用中...

    论文研究-B树索引机制的研究及优化.pdf

    当数据庞杂时,B 树索引在查找效率和空间利用率方面还存在不足。针对该问题提出一种改进的B 树结构,首先通过调整叶子节点与非叶子节点的数量关系,以降低树的深度;然后优化原插入算法,在分裂节点前进行平衡处理...

    B和B+树索引文件的异同

    B 和 B+树是数据库和文件系统中常用的索引数据结构,它们都是从平衡树理论中派生出来的,主要用于高效地存储和检索大量数据。两者的主要区别在于数据的分布和查询方式。 1. B 树(Balanced Tree): - B 树是一种...

    数据库中B+树索引的原理

    **数据库中的B+树索引原理** B+树(B Plus Tree)是一种高效的数据结构,广泛应用于数据库系统中,主要用于实现快速的索引查询。它优化了传统的二叉搜索树,能够有效地处理大规模数据,尤其是在磁盘存储环境中,极...

    B树索引实现歌曲管理系统 qt界面

    在本项目中,"B树索引实现歌曲管理系统qt界面"是一个使用了数据结构和图形用户界面技术的软件工程实践。下面将详细解释这个系统的关键组成部分及其相关知识点。 首先,我们要理解**B树(B-Tree)**。B树是一种自...

    B+树作为数据库的索引

    下面我们将深入探讨B+树的基本概念、特性以及它如何提高数据库查询效率。 **B+树的结构与特性:** 1. **节点类型**:B+树包含根节点、内部节点(也称分支节点)和叶子节点。所有数据都存储在叶子节点上,非叶子节点...

    B树实现的文件索引 java版

    在这个项目“B树实现的文件索引 java版”中,我们探讨的是如何利用Java语言来实现B树在文件索引中的应用。 B树是一种多路搜索树,它的每个节点可以有多个子节点,通常比二叉树更适合处理大量的数据。这种数据结构的...

    为什么说B+树比B树更适合做文件索引

    为什么说B+树比B树更适合做文件索引

    B.rar_B-树索引_B树_b tree_b tree java_java B-Tree

    总之,这个压缩文件提供了一个深入了解和实践B-树索引的好机会,通过学习和分析Java代码,不仅可以掌握B-树的基本原理,还能提高编程技能,对数据库和文件系统的设计有更深入的理解。对于计算机科学的学生、软件...

    索引介绍聚集索引和非聚集索引

    索引通过创建一种数据结构(例如B树)来实现这一点,这种结构允许数据库管理系统能够快速定位到数据所在的物理位置。 #### 二、B+树结构 B+树是一种自平衡的树数据结构,常用于数据库索引。B+树的特点在于所有的...

    B树索引和自创Connect.rar

    在数据库管理系统中,索引是一种优化查询性能的重要技术。这里我们主要探讨两种索引结构:B树(B-Tree)和一种自创的Connect结构。...通过源码分享,我们可以深入研究这些数据结构的实现,进一步提升自己的技术能力。

    book-mis.rar_B+树索引_b树系统

    在给定的"book-mis.rar_B+树索引_b树系统"中,我们可以看到一个专门针对图书管理的系统,它利用了B+树(B Plus Tree)索引来优化数据操作,如查找、排序、添加和删除。这里我们将详细探讨B+树索引及其在图书管理系统...

Global site tag (gtag.js) - Google Analytics