数据结构-树的进化及与数据库的关系
硬盘知识
我们都知道内存比外存(硬盘)存储空间要小,因为内存的材料要比硬盘贵很多,所以我们把不常用数据(比如mysql数据)保存在硬盘中,当需要使用的时候通过总线传输到内存中,我们常使用的硬盘都是机械硬盘,查找数据的时候是有时间消耗的,所以我们在查找数据时尽量保证尽可能少的次数就可以把想要的数据查找到,那么下面我们来了解一下硬盘的原理:
磁盘是一个扁平的圆盘(与电唱机的唱片类似)。盘面上有许多称为磁道的圆圈,数据就记录在这些磁道上。磁盘可以是单片的,也可以是由若干盘片组成的盘组,每一盘片上有两个面,如上图:除去最顶端和最底端的外侧面不存储数据之外,一共有10个面可以用来保存信息。
当磁盘驱动器执行读/写功能时。盘片装在一个主轴上,并绕主轴高速旋转,当磁道在读/写头(又叫磁头) 下通过时,就可以进行数据的读 / 写了。
活动头盘 (如上图)的磁头是可移动的。每一个盘面上只有一个磁头(磁头是双向的,因此正反盘面都能读写)。它可以从该面的一个磁道移动到另一个磁道。所有磁头都装在同一个动臂上,因此不同盘面上的所有磁头都是同时移动的(行动整齐划一)。当盘片绕主轴旋转的时候,磁头与旋转的盘片形成一个圆柱体。各个盘面上半径相同的磁道组成了一个圆柱面,我们称为柱面 。因此,柱面的个数也就是盘面上的磁道数。
磁盘的读/写原理和效率
磁盘上数据必须用一个三维地址唯一标示:柱面号、盘面号、块号(磁道上的盘块)
读/写磁盘上某一指定数据需要下面3个步骤:
首先移动臂根据柱面号使磁头移动到所需要的柱面上,这一过程被称为定位或查找 。
所有磁头都定位到了10个盘面的10条磁道上(磁头都是双向的)。这时根据盘面号来确定指定盘面上的磁道
盘面确定以后,盘片开始旋转,将指定块号的磁道段移动至磁头下。
经过上面三个步骤,指定数据的存储位置就被找到。这时就可以开始读/写操作了。
访问某一具体信息,由3部分时间组成:
查找时间(seek time) Ts: 完成上述步骤(1)所需要的时间。这部分时间代价最高,最大可达到0.1s左右。
等待时间(latency time) Tl: 完成上述步骤(3)所需要的时间。由于盘片绕主轴旋转速度很快,一般为7200转/分(电脑硬盘的性能指标之一, 家用的普通硬盘的转速一般有5400rpm(笔记本)、7200rpm几种)。因此一般旋转一圈大约0.0083s。
传输时间(transmission time) Tt: 数据通过系统总线传送到内存的时间,一般传输一个字节(byte)大概0.02us=2*10^(-8)s
磁盘读取数据是以盘块 (block) 为基本单位的。 位于同一盘块中的所有数据都能被一次性全部读取出来。单就时间成本而言,读取物理地址连续的 1000 个字节,与读取单个字节几乎没有区别。而磁盘IO代价主要花费在查找时间Ts上。因此我们应该尽量将相关信息存放在同一盘块,同一磁道中。或者至少放在同一柱面或相邻柱面上,以求在 读/写信息时尽量减少磁头来回移动的次数,避免过多的查找时间Ts。
每个结点孩子个数可以 > 2,多路就是指子树超过 2 个;
每个结点可以存储多个数据元素,即容纳多个值;
除非数据已经填满,否则不会增加新的层。也就是说,B树追求"层"越少越好。
子结点中的值,与父结点中的值,有严格的大小对应关系。一般来说,如果父结点有a个值,那么就有a+1个子结点。比如,父节点有两个值(7和16),就对应三个子结点,第一个子结点都是小于7的值,最后一个子结点都是大于16的值,中间的结节点就是7和16之间的值。
歧义:B-树,即为B树。因为B树的原英文名称为B-tree,而国内很多人喜欢把B-tree译作B-树,其实,这是个非常不好的直译,很容易让人产生误解。如人们可能会以为B-树是一种树,而B树又是一种树。而事实上是,B-tree就是指的B树。
提高效率:原理
每个节点存放多个元素,降低树的深度,避免树的深度过大导致磁盘IO过于频繁而导致查询效率低下。
每一次磁盘的访问我们都可以获得最大量的数据。每个节点可以具有比二叉树多得多的元素,减少了必须访问节点和数据块的数量,从而提高了性能。可以说B树的数据结构就是为内外存的数据交互准备的。
所以根据以上讲解,当我们在设计B树结构的时候,可以将B树中每个结点存放数据的个数与硬盘的盘块容量做匹配,保证一次性获取尽可能多的数据,获取数据后在内存中我们可以使用拆半查找等算法进行内存查找操作。
意义:
文件查找的具体过程(涉及磁盘IO操作),为了简单,上图用少量数据构造一棵3叉树的形式,实际应用中的B树结点中关键字很多的。上图中比如根结点,其中17表示一个磁盘文件的文件名;小红方块表示这个17文件内容在硬盘中的存储位置;p1表示指向17左子树的指针。
假如每个盘块可以正好存放一个B树的结点(正好存放2个文件名)。那么一个结点就代表一个盘块,而子树指针就是存放另外一个盘块的地址。
模拟查找文件29的过程:
根据根结点指针找到文件目录的根磁盘块1,将其中的信息导入内存。【磁盘IO操作 1次】
此时内存中有两个文件名17、35和三个存储其他磁盘页面地址的数据。根据算法我们发现:17<29<35,因此我们找到指针p2。
根据p2指针,我们定位到磁盘块3,并将其中的信息导入内存。【磁盘IO操作 2次】
此时内存中有两个文件名26,30和三个存储其他磁盘页面地址的数据。根据算法我们发现:26<29<30,因此我们找到指针p2。
根据p2指针,我们定位到磁盘块8,并将其中的信息导入内存。【磁盘IO操作 3次】
此时内存中有两个文件名28,29。根据算法我们查找到文件名29,并定位了该文件内存的磁盘地址。
分析上面的过程,发现需要3次磁盘IO操作和3次内存查找操作。关于内存中的文件名查找,由于是一个有序表结构,可以利用折半查找提高效率。至于IO操作是影响整个B树查找效率的决定因素。
如果我们使用平衡二叉树的磁盘存储结构来进行查找,磁盘4次,最多5次,而且文件越多,B树比平衡二叉树所用的磁盘IO操作次数将越少,效率也越高。
2-3 树、2-3-4 树都是B树的特例。
2-3 树:
是一种多路查找树;
每个结点都具有两个孩子或三个孩子;
具有两个孩子的结点称为2结点,具有三个孩子的结点称为3结点;
一个2结点包含一个元素,如果有孩子就有两个,否则无孩子,并且左孩子结点值 < 根,右 > 根;
一个3结点包含两个元素,如果有孩子就有三个,否则无孩子,并且左孩子结点值 < 较小的,右 > 较大的,较小值 < 中间孩子 < 较大值;
所有叶子在 同一层 上;
插入:
2-3-4 树:
每个节点都具有2个孩子(称之为2节点)或者3个孩子(称之为3节点)或者4个孩子(称之为4节点)。
一个2节点包含一个元素和两个孩子(或没有孩子)。
一个3节点包含两个元素和三个孩子(或没有孩子)。
一个4节点包含三个元素和四个孩子(或没有孩子)。
B+树,也是一种多路搜索书:
B树的变种;
分支结点只存索引,不存具体数据;
叶子结点包含所有数据,并且包含叶子结点本身按着关键字大小自小而大顺序连接;
为什么说B+-tree比B 树更适合实际应用中操作系统的文件索引和数据库索引?
B+-tree的磁盘读写代价更低
B+-tree 的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。举个例子,假设磁盘中的一个盘块容纳16bytes,而一个关键字2bytes,一个关键字具体信息指针2bytes。一棵9阶B-tree(一个结点最多8个关键字)的内部结点需要2个盘快。而B+ 树内部结点只需要1个盘快。当需要把内部结点读入内存中的时候,B 树就比B+ 树多一次盘块查找时间(在磁盘中就是盘片旋转的时间)。
B+-tree的查询效率更加稳定,由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。