B-树
1 .B-树定义
B-树是一种平衡的多路查找树,它在文件系统中很有用。
定义:一棵m 阶的B-树,或者为空树,或为满足下列特性的m 叉树:
⑴树中每个结点至多有m 棵子树;
⑵若根结点不是叶子结点,则至少有两棵子树;
⑶除根结点之外的所有非终端结点至少有[m/2] 棵子树;
⑷所有的非终端结点中包含以下信息数据:
其中:Ki(i=1,2,…,n)为关键码,且Ki<Ki+1,
⑸所有的叶子结点都出现在同一层次上,并且不带信息(可以看作是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)。
B-树的查找类似二叉排序树的查找,所不同的是B-树每个结点上是多关键码的有序表,在到达某个结点时,先在有序表中查找,若找到,则查找成功;否则,到按照对应的指针信息指向的子树中去查找,当到达叶子结点时,则说明树中没有对应的关键码。
在上图的B-树上查找关键字47的过程如下:
1)首先从更开始,根据根节点指针找到 *节点,因为 *a 节点中只有一个关键字,且给定值47 > 关键字35,则若存在必在指针A1所指的子树内。
2)顺指针找到 *c节点,该节点有两个关键字(43和 78),而43 < 47 < 78,若存在比在指针A1所指的子树中。
3)同样,顺指针找到 *g节点,在该节点找到关键字47,查找成功。
2. 查找算法
- typedef
int KeyType ; - #define
m 5 - typedef
struct Node{ -
int keynum; -
struct Node *parent; -
KeyType key[m+1]; -
struct Node *ptr[m+1]; -
Record *recptr[m+1]; - }NodeType;
-
- typedef
struct{ -
NodeType *pt; -
int i; -
int tag; - }Result;
-
- Result
SearchBTree(NodeType *t,KeyType kx) - {
-
-
-
-
p=t;q=NULL;found=FALSE;i=0; -
while(p&&!found) -
{ n=p->keynum;i=Search(p,kx); -
if(i>0&&p->key[i]= =kx) found=TRUE; -
else {q=p;p=p->ptr[i];} -
} -
if(found) return (p,i,1); -
else return (q,i,0); - }
B- 树查找算法分析
从查找算法中可以看出, 在B- 树中进行查找包含两种基本操作:
3.B-树的插入
如图(a) 为3阶的B-树(图中略去F结点(即叶子结点)),假设需依次插入关键字30,26,85。
1) 首先通过查找确定插入的位置。由根*a 起进行查找,确定30应插入的在*d 节点中。由于*d 中关键字数目不超过2(即m-1),故第一个关键字插入完成:如(b)
2) 同样,通过查找确定关键字26亦应插入 *d. 由于*d节点关键字数目超过2,此时需要将 *d分裂成两个节点,关键字26及其前、后两个指针仍保留在 *d 节点中,而关键字37 及其前、后两个指针存储到新的产生的节点 *d` 中。同时将关键字30 和指示节点 *d `的指针插入到其双亲的节点中。由于 *b节点中的关键字数目没有超过2,则插入完成.如(c)(d)
3) (e) -(g) 为插入85后;
插入算法:
- int
InserBTree(NodeType **t,KeyType kx,NodeType *q,int i){ -
-
-
x=kx;ap=NULL;finished=FALSE; -
while(q&&!finished) -
{ -
Insert(q,i,x,ap); -
if(q->keynum<m) finished=TRUE; -
else -
{ -
s=m/2;split(q,ap);x=q->key[s]; -
-
q=q->parent; -
if(q) i=Search(q,kx); -
} -
} -
if(!finished) -
NewRoot(t,q,x,ap); - }
因此,下面我们可以只需讨论删除最下层非终端结点中的关键字的情形。有下列三种可能:
[例如],从图图4.2( a)中删去50,需将其右兄弟结点中的61上移至*e结点中,而将*e结点中的53移至*f,从而使*f和*g中关键字数目均不小于ceil(m-1)-1,而双亲结点中的关键字数目不变,如图图4.2(b)所示。
[例如],从图4.2(b)所示 B-树中删去53,则应删去*f结点,并将*f中的剩余信息(指针“空”)和双亲*e结点中的 61一起合并到右兄弟结点*g中。删除后的树如图4.2(c)所示。
[例如],在
B-树主要应用在文件系统
为了将大型数据库文件存储在硬盘上 以减少访问硬盘次数为目的 在此提出了一种平衡多路查找树——B-树结构 由其性能分析可知它的检索效率是相当高的 为了提高
B+树
树的差异在于:
⑴有n 棵子树的结点中含有n 个关键码;
⑵所有的叶子结点中包含了全部关键码的信息,及指向含有这些关键码记录的指针,且
叶子结点本身依关键码的大小自小而大的顺序链接。
⑶所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键码。
树,不管查找成功与否,每次查找都是走了一条从根到叶子结点的路径。
B+树在数据库中的应用
1.
2. B+树在数据库索引中的应用
目前大部分数据库系统及文件系统都采用B-Tree或其变种B+Tree作为索引结构
1)在数据库索引的应用
在数据库索引的应用中,B+树按照下列方式进行组织
①
②
2)B+树索引的插入和删除
①在向数据库中插入新的数据时,同时也需要向数据库索引中插入相应的索引键值 ,则需要向 B+树
②当从数据库中删除数据时,同时也需要从数据库索引中删除相应的索引键值 ,则需要从 B+树 中删
为什么使用B-Tree(B+Tree)
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。为什么使用B-/+Tree,还跟磁盘存取原理有关。
由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:
当一个数据被用到时,其附近的数据也通常会马上被使用。
程序运行期间所需要的数据通常比较集中。
由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。
预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。
B-Tree中一次检索最多需要h-1次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logmN)。一般实际应用中,m是非常大的数字,通常超过100,因此h非常小(通常不超过3)。
综上所述,用B-Tree作为索引结构效率是非常高的。
而红黑树这种结构,h明显要深的多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,所以红黑树的I/O渐进复杂度也为O(h),效率明显比B-Tree差很多。
MySQL的B-Tree索引(技术上说B+Tree)
下面主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式:
1. MyISAM索引实现:
1)主键索引:
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM主键索引的原理图:
这里设表一共有三列,假设我们以Col1为主键,图myisam1是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。
2)辅助索引(Secondary key)
在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。如果我们在Col2上建立一个辅助索引,则此索引的结构如下图所示:
同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。
MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。
2. InnoDB索引实现
然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同.
1)主键索引:
(图inndb主键索引)是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
2). InnoDB的辅助索引
一是主索引的区别,InnoDB的数据文件本身就是索引文件。而MyISAM的索引和数据是分开的。
二是辅助索引的区别:InnoDB的辅助索引data域存储相应记录主键的值而不是地址。而MyISAM的辅助索引和主索引没有多大区别。