精华内容
下载资源
问答
  • B-Tree Filer 是一个用于 Borland Turbo Pascal & Delphi 的基于文件的数据库例程的快速库。 B-Tree Filer 支持独立程序或在 Microsoft 兼容网络(包括 Novell Netware)上运行的程序。
  • 本文将详细介绍SQLite中的B-Tree实现细节,需要了解更多的朋友可以参考下
  • B-TreeB+Tree

    千次阅读 2019-08-12 10:49:48
    目前大部分数据库系统及文件系统都采用B-Tree或其变种B+Tree作为索引结构,在本文的下一节会结合存储器原理及计算机存取原理讨论为什么B-TreeB+Tree在被如此广泛用于索引,这一节先单纯从数据结构角度描述它们。...

    目前大部分数据库系统及文件系统都采用B-Tree或其变种B+Tree作为索引结构,在本文的下一节会结合存储器原理及计算机存取原理讨论为什么B-Tree和B+Tree在被如此广泛用于索引,这一节先单纯从数据结构角度描述它们。

    B-Tree

    为了描述B-Tree,首先定义一条数据记录为一个二元组[key, data],key为记录的键值,对于不同数据记录,key是互不相同的;data为数据记录除key外的数据。那么B-Tree是满足下列条件的数据结构:

    d为大于1的一个正整数,称为B-Tree的度。

    h为一个正整数,称为B-Tree的高度。

    每个非叶子节点由n-1个key和n个指针组成,其中d<=n<=2d。

    每个叶子节点最少包含一个key和两个指针,最多包含2d-1个key和2d个指针,叶节点的指针均为null 。

    所有叶节点具有相同的深度,等于树高h。

    key和指针互相间隔,节点两端是指针。

    一个节点中的key从左到右非递减排列。

    所有节点组成树结构。

    每个指针要么为null,要么指向另外一个节点。

    如果某个指针在节点node最左边且不为null,则其指向节点的所有key小于v(key1),其中v(key1)为node的第一个key的值。

    如果某个指针在节点node最右边且不为null,则其指向节点的所有key大于v(keym),其中v(keym)为node的最后一个key的值。

    如果某个指针在节点node的左右相邻key分别是keyi和keyi+1且不为null,则其指向节点的所有key小于v(keyi+1)且大于v(keyi)。

    图2是一个d=2的B-Tree示意图。

    图2

    由于B-Tree的特性,在B-Tree中按key检索数据的算法非常直观:首先从根节点进行二分查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或找到null指针,前者查找成功,后者查找失败。B-Tree上查找算法的伪代码如下:

    BTree_Search(node, key) {
        if(node == null) return null;
        foreach(node.key)
        {
            if(node.key[i] == key) return node.data[i];
                if(node.key[i] > key) return BTree_Search(point[i]->node);
        }
        return BTree_Search(point[i+1]->node);
    }
    data = BTree_Search(root, my_key);
    

    关于B-Tree有一系列有趣的性质,例如一个度为d的B-Tree,设其索引N个key,则其树高h的上限为logd((N+1)/2),检索一个key,其查找节点个数的渐进复杂度为O(logdN)。从这点可以看出,B-Tree是一个非常有效率的索引数据结构。

    另外,由于插入删除新的数据记录会破坏B-Tree的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持B-Tree性质,本文不打算完整讨论B-Tree这些内容,因为已经有许多资料详细说明了B-Tree的数学性质及插入删除算法,有兴趣的朋友可以在本文末的参考文献一栏找到相应的资料进行阅读。

    B+Tree

    B-Tree有许多变种,其中最常见的是B+Tree,例如MySQL就普遍使用B+Tree实现其索引结构。

    与B-Tree相比,B+Tree有以下不同点:

    每个节点的指针上限为2d而不是2d+1。

    内节点不存储data,只存储key;叶子节点不存储指针。

    图3是一个简单的B+Tree示意。

    图3

    由于并不是所有节点都具有相同的域,因此B+Tree中叶节点和内节点一般大小不同。这点与B-Tree不同,虽然B-Tree中不同节点存放的key和指针可能数量不一致,但是每个节点的域和上限是一致的,所以在实现中B-Tree往往对每个节点申请同等大小的空间。

    一般来说,B+Tree比B-Tree更适合实现外存储索引结构,具体原因与外存储器原理及计算机存取原理有关,将在下面讨论。

    带有顺序访问指针的B+Tree

    一般在数据库系统或文件系统中使用的B+Tree结构都在经典B+Tree的基础上进行了优化,增加了顺序访问指针。

    图4

    如图4所示,在B+Tree的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree。做这个优化的目的是为了提高区间访问的性能,例如图4中如果要查询key为从18到49的所有数据记录,当找到18后,只需顺着节点和指针顺序遍历就可以一次性访问到所有数据节点,极大提到了区间查询效率。

    这一节对B-Tree和B+Tree进行了一个简单的介绍,下一节结合存储器存取原理介绍为什么目前B+Tree是数据库系统实现索引的首选数据结构。

    展开全文
  • MySQL Hash索引和B-Tree索引的区别究竟在哪里呢?相信很多人都有这样的疑问,下文对两者的区别进行了详细的分析,需要的朋友可以参考下
  • B-TreeB+Tree详解

    2019-08-05 16:58:38
    B-Tree详解 B-Tree由来 我们前面已经了解了平衡二叉查找树,如果100w条数据放入到二叉查找树中,假设树的高度为n,则2^0+2^1+2^2+2^3...+2^(n-1)=(2^n)-1=1000000,计算出来n为20,因此查找100w条数据中的一个...

    B-Tree详解

    B-Tree由来

    我们前面已经了解了平衡二叉查找树,如果100w条数据放入到二叉查找树中,假设树的高度为n,则2^0+2^1+2^2+2^3...+2^(n-1)=(2^n)-1=1000000,计算出来n为20,因此查找100w条数据中的一个数据时最多需要查询20次即可。如果是在内存操作的话,查询效率还是蛮高的,但是数据库中的数据都是存放在磁盘上的,每读取一个节点就需要一次磁盘IO,这样子的话查找100w条数据中的一个数据时最多需要20次磁盘IO,20次磁盘IO,这个性能对于磁盘来说太慢了。那么有什么好的方法来解决这个问题呢?答案就是使用B-Tree,即将这棵树压缩一下,减少树的高度,每层上可以容纳更多的节点,这样子就会减少磁盘IO次数从而提高性能

    B-Tree特性

    假如有一颗 m 阶的B-Tree,则有如下特性:

    1、每个节点最多有 m 个子节点

    2、除了根节点和叶子结点外,每个节点至少有 m/2 (向上取整,例如 3/2 = 1.5,这里取 2) 个子节点

    3、若根节点不是叶子节点,那么根节点至少有2个子节点

    4、所有的叶子都在同一层上

    5、每个节点都包含 n 个 key,其中 m/2-1 <= n <= m-1

    6、每个节点中的元素 key 从小到大排列,元素 key 的左节点的所有元素 key 值都小于等于元素 key,右节点的所有元素 key 值都大于等于元素 key

    从上述特性中可知 B-Tree 和其他树结构相比, B-Tree 每个节点可以容纳多个元素。这些特性理解起来比较费事,下面通过示例来让大家对 B-Tree 有一个清晰的认知

    B-Tree示例

    假如我们要生成一颗 4 阶的 B-Tree树,其过程如下

    1、先插入 1、2、3,如下图:

    2、当插入4 元素时,根据特性5可知,4/2-1 <= n <= 3,即 1<=n<=3,也就是说每个节点最多可以包含 3个元素,此时树的结构需要裂变了,裂变的原则是:节点中的中间元素上移,例如节点中有4个元素,那么第2个或者第3个元素上移都可以,如果有5个元素时,则第3个元素上移就可以了,其他元素按照特性6排列。具体过程如下图:

    裂变之后,根节点有2个子节点,符合特性3,当根节点不是叶子节点的时候肯定是发生了裂变

    3、插入5、6之后,同理步骤2:

    4、插入7、8之后,同理,步骤2:

    5、插入9、10之后会怎么样呢?同样是同理步骤2,如下图:

    从上述过程可以看出,B-Tree 是从下往上形成的,而二叉树是从上往下形成的。

    B-Tree树特性详解

    特性1:当父节点元素达到m-1个时,其子节点个数达到m个时,同时这些子节点中的元素达到 m 个时,节点将会裂变,裂变之后父节点元素达到了m个,父节点继续裂变,裂变之后其子节点数量为 m-1 个,因此每个节点最多有 m 个子节点,即特性1

    下面就特性1简单演示一下,以便于理解:

    特性2:当节点中的元素达到 m 个时,节点将会裂变,裂变之后子节点个数最少是 m/2 (向上取整) 个,即特性2

    特性5:当节点中的元素达到 m 个时,节点将会裂变,裂变之后每个子节点中的元素最少是 m/2-1(原节点元素平分) 个,那么为什么不是 m/2,而是 m/2-1呢,-1是因为有一个节点裂变之后上移成为父节点了,裂变之后每个子节点中的元素最多则是 m-1 个,即特性5

    B-Tree高度计算

    若一颗 m 阶B-Tree,总元素有 N 个,高度为 h,则有如下推导公式:

    1、树的高度:1,2,3,4,.........,h

    2、高度对应的节点数:1,2*(m/2)^0,2*(m/2)^1,2*(m/2)^2,.........,2*(m/2)^(h-2)

    3、总结点数 s = 1 + 2*((m/2)^(h-1) -1)/((m/2) -1)

    4、总元素 N = 1 + (s -1) * ((m/2) - 1) = 2*((m/2)^(h-1)) - 1

    5、高度 h = log[m/2]((N+1)/2) + 1

    数据库怎么使用B-Tree存储数据

    从上面介绍我们知道B-Tree中都是存储的数值,而数据库中存储的却是一条条的数据,那么若某数据库是以B-Tree数据结构存储数据的,那么数据是怎么存放的呢?看下图所示:

    上图中,我们把元素拆分成了 key-data 的形式,Key 就是数据的主键,Data 就是具体的数据

    这样我们在查找一条数据的时候,就沿着根结点往下找 key 就 OK 了,效率是比较高的,查找到key就直接可以获取对应的数据data了

    B+Tree详解

    B+Tree由来

    B+Tree 是 B-Tree 基础上的一种优化,使其更适合实现外存储索引结构,InnoDB(mysql存储引擎)存储引擎就是使用 B+Tree 存储索引结构的。既然已经有了B-Tree,那么为什么还需要有B+Tree呢?主要是由于同等数据量的数据存储到 B+Tree 中时,树的高度更低,查询效率更高,这个后面讲解

    B+Tree特性

    1、所有非叶子节点只存储 key 信息,不存储 data(数据信息)

    2、所有 data (数据信息)都存储在叶子节点中

    3、所有叶子节点之间都有一个链指针

    4、所有叶子节点包含了全部元素(key+data)的信息

    B+Tree 示例

    这里将 B-Tree 的数据存储图变成 B+Tree 之后,如下图:

    图上可以看出:

    非叶子节点只存储了 key(关键字)信息,即特性1

    所有的 data(数据)信息都保存在了叶子节点中,即特性2

    所有的叶子节点之间都有一个链指针,即特性3

    所有叶子节点包含所有的数据(key + data)信息

    特性3(节点链指针)优点:为了提高区间访问的性能,上图中如果要查询key为[3,7]之间的所有数据记录,当找到3后,只需顺着节点和指针顺序遍历就可以一次性访问到所有数据节点,极大提到了区间查询效率

    B+Tree 和 B-Tree 对比

    1、同等树高时,B-Tree 查询效率比 B+Tree 高,由于 B-Tree 节点中存储了 key+data 的整个信息,找到 key 就等于是获取到了数据,而 B+Tree 的 data 只存储在叶子节点, 因此必须要遍寻到叶子节点才能获取到 data 信息

    2、同等数据量时,B+Tree 的查询效率比  B-Tree 高,由于 B+Tree 的非叶子节点只存储 key 信息,而 B-Tree 的节点存储了 key + data,而每个页(一个节点)的大小是固定的,所以 B+Tree 的树高会更低一些

    为了更直观的让大家理解,下面通过两张图来对比一下:

    从上图可以看出 同样的 22 个数据,B-Tree 的树高是 4,而 B+Tree 的树高是 3

    结论:数据量越大,B+Tree的层高优势就越明显了

    3、查找大于或者小于某一个 key(关键字) 的数据时,B+Tree 的效率远远高于 B-Tree,由于 B+Tree 的叶子节点存储了所有 key+data 信息,而且叶子节点之间有链指针,所有查询的时候 B+Tree 只需要找到 key 然后沿着链表遍历就可以了,而 B-Tree就需要一遍遍的从根节点开始查找

    为了更直观的让大家理解,下面也通过两张图来对比一下:

    B-Tree查询大于8的数据:

    B+Tree 查询大于8的数据:

    从上图对比可知,B+Tree 的效率远远高于 B-Tree

    结论:通过以上3点对比可以得知常用的关系型数据库中,都是会选择 B+Tree来存储数据的

    B-Tree和B+Tree网页版生成链接地址

    B-Tree:https://www.cs.usfca.edu/~galles/visualization/BTree.html

    B+Tree:https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html

    展开全文
  • 问题由来:索引:大家平常说的还有用的索引,如果没特别标明或者声明都是B-Tree索引,大多数Mysql引擎都支持这种索引,而Msyql常用引擎InnoDB等常为B+-Tree。提出问题:!!!注意是BB树 不是读作B减树 B-Tree,...

    问题由来:

    索引:大家平常说的还有用的索引,如果没特别标明或者声明都是B-Tree索引,大多数Mysql引擎都支持这种索引,而Msyql常用引擎InnoDB等常为B+-Tree。

    提出问题:

    !!!注意是B树 B树 不是读作B减树 B-Tree,怎么读心里要有B数的。

    WTF,B-Tree是什么Tree,是BinaryTree么?并不是那么什么是B+-Tree呢?有什么区别?我们平常用的二叉搜索树的时间复杂度不是Log(n)么,难道不够优秀么?

    开始解决问题:

    预备知识

    磁盘IO

    · 系统读取磁盘是将磁盘的基本单位---磁盘块(Block)读取出来。位一同一磁盘块的数据会被一次性读取出来。磁盘读取IO是机械动作,时间大概为内存读取的十万多倍。所以磁盘IO读写速度成为索引性能的主要指标。

    1.复习一下二叉搜索树(Binay Search Tree,即BST)

    如图,为二叉搜索树,它的时间复杂度为O(Log(n))。我们现在要执行搜索,那么考虑下最好的情况就是,搜索0009,也就是搜索的关键字为BST的根节点,读取一次IO;那么最坏的情况显而易见就是树最深的底层叶子节点(深度为N那么为N次磁盘IO)。

    例如 :要索引0010,那么要进行四次磁盘IO操作,这已经比其他数据结构少很多次了。

    能不能优化,怎么优化

    · 如果优化要优化BST最坏的情况

    · 最坏情况是由树的深度决定的,也就是说它是N阶BST,那么我们要压缩它

    · 压缩 ?什么意思? 最近世界杯(以英格兰为例子),就是把BST(大竹竿克劳奇)变成B-Tree(小土豆鲁尼)

    左为鲁尼,右边为克劳奇~

    2 由之前的思考,引出了B-Tree:

    B-Tree---平衡多路查找树

    · B-Tree是为磁盘等外存储设备设计的一种平衡查找树。

    · B-Tree结构的数据可以让系统高效的找到数据所在的磁盘块。为了描述B-Tree,首先定义一条记录为一个二元组[key, data],key为记录的键值,对应表中的主键值,data为一行记录中除主键外的数据。对于不同的记录,key值互不相同。(可以以python中的字典中的键值对理解,键唯一且不可改变,就是一个索引的标记,而内容存在data里面)

    一棵m阶的B-Tree有如下特性:

    每棵树都有好多属性概念,这里加粗的是我们用的,是对我们理解B-Tree有关的。

    1. 每个节点最多有m个孩子。

    1. 除了根节点和叶子节点外,其它每个节点至少有Ceil(m/2)个孩子。
    2. 若根节点不是叶子节点,则至少有2个孩子
    3. 所有叶子节点都在同一层,且不包含其它关键字信息
    4. 每个非终端节点包含n个关键字信息(P0,P1,…Pn, k1,…kn)
    5. 关键字的个数n满足:ceil(m/2)-1 <= n <= m-1
    6. ki(i=1,…n)为关键字,且关键字升序排序。
    7. Pi(i=1,…n)为指向子树根节点的指针。P(i-1)指向的子树的所有节点关键字均小于ki,但都大于k(i-1)

    直接上图,这就是一个B-Tree,紫色为Key,黄色为data,蓝色为指针,仔细看发现会有磁盘块,结合之前讲的磁盘IO操作,可以梳理清楚B-Tree的索引流程

    每个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3指针指向的子树的数据范围为大于35。

    例子查找60

    模拟查找关键字60的过程:

    根据根节点找到磁盘块1,读入内存。【磁盘I/O操作第1次】比较关键字60大于区间(17,35),找到磁盘块1的指针P3。根据P3指针找到磁盘块4,读入内存。【磁盘I/O操作第2次】比较关键字60小于在区间(65,87),找到磁盘块3的指针P1。根据P2指针找到磁盘块9,读入内存。【磁盘I/O操作第3次】在磁盘块9中的关键字列表中找到关键字60。分析上面过程,发现需要3次磁盘I/O操作,和3次内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素。B-Tree使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。

    !!!:注意比之前BST多了在每一个磁盘页的索引比较,但是因为磁盘页已经被磁盘IO读取操作读入了内存中,所以内存IO操作比磁盘IO操作省时很多根本不是一个数量级的可以忽略不计,所以磁盘IO操作仍然是最重要的索引性能关键。

    显而易见,这次我们进行了三次磁盘IO比之前的BST少了一次,试想一下在复杂的B-Tree索引中,会减少很多次磁盘IO操作,所以B-Tree更适合索引。

    那么还可以再优化么?

    iphone6/7/8 升级了就是 iphone6+/7+/8+ ,是的加个+(plus),看来都这么干。

    B+-Tree:

    B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。

    之前我们记得B-Tree的索引和关键字key-data对存在磁盘里面,然后被磁盘IO操作读入内存,那么在磁盘页中,如果数据很大呢,如果是大数据呢!!!!

    如果数据大的话,磁盘页无法装载,会使得一页的key的数量减少,还是会使得B-Tree的深度增加,这样还是会增加磁盘IO查询,计算机科学的老前辈就有一个大胆的想法,那么我把数据全部放在Tree的叶子节点呢。

    在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。

    B+Tree相对于B-Tree有几点不同:

    非叶子节点只存储键值信息。所有叶子节点之间都有一个链指针。数据记录都存放在叶子节点中。

    B+-Tree图

    通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。

    图中为聚集索引(clustered index),即范围查找,从跟节点开始。

    !!!B-Tree与B+-Tree的区别!!!

    · B+Tree的磁盘读写更低,因为非叶子节点可以存储更多的索引key,而key索引在同一层更集中,那么会降低磁盘IO读写次数。

    · B+Tree的查询效率更稳定,由于最终指向文件内容的节点是叶子节点中关键字的索引,所以任何关键只查找都必须走从根节点到叶子节点的索引路径,路径是相似的(深度),所以更稳定(最好最坏情况都在最底层)。

    · 区间访问性友好Mysql是关系型数据库,所以经常会按照区间来访问某个索引,B+树的叶子节点会按顺序建立起链状指针,加强区间访问性。

    最后一个问题:

    也是最重要的为什么Mysql会使用B/B+树来实现索引呢?

    · Mysql是基于磁盘的数据库,索引是以索引文件的形式转存于磁盘中的

    · 索引的过程就是要涉及磁盘IO的消耗,磁盘IO消耗比内存IO消耗要搞好几个数量级, 即索引涉及的在查找关键字时尽量减少磁盘IO消耗。

    附加问题:

    操作系统、数据库、或者说Mysql数据库引擎为什么使用B+-Tree的多呢?

    · 因为B-Tree仍然未解决关键字的遍历,如果要遍历B-Tree上的所以关键字时间复杂度非常高,但是B+-Tree就不一样了,可以直接遍历叶子节点,支持范围索引,因为OS DB DB ENGINE 都经常使用范围索引,故 B+-Tree更胜一筹。

    打完收工~


    作者:RockyChen
    链接:https://juejin.im/post/5b25bc1051882574b00fb7da
    来源:掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    展开全文
  • 深入解析了Mysql的B+Tree索引底层数据结构,以及MyISAM和InnoDB 存储引擎的索引底层原理。

    深入解析了Mysql的B+Tree索引底层数据结构,以及MyISAM和InnoDB 存储引擎的索引底层原理。

    上一篇文章中,我们介绍了索引的概念以及MySQL常见索引类型:索引的概念以及MySQL七种索引类型。下面我们来看看常见的索引结构的底层实现原理。包括B-Tree、B+Tree的数据结构,以及MyISAM和InnoDB 存储引擎对于B+Tree索引的具体实现。

    1 索引的数据结构

    索引有多种数据结构,在MySQL中,索引是在存储引擎层而不是服务器层实现的。所以,并没有统一的索引标准:不同存储引擎的索引的工作方式不一样,也不是所有的存储引擎都支持所有类型的索引。即使多个存储引擎支持同一类型的索引,其底层实现也可能不同。

    一般来说常见的索引结构是HASH和BTREE
    在这里插入图片描述

    2 B-Tree索引的介绍

    如果在谈论索引的时候没有指明类型,那么多半说的是B-Tree索引,它使用B-Tree数据结构来存储数据,大多数存储引擎都支持这种索引。

    不过,虽然大多都支持B-Tree索引,但是底层的存储引擎也可能使用不同的存储结构,例如NDB集群存储引擎内部实际上使用了T-Tree结构存储这种索引,即使其名字是BTREE;而InnoDB和MyISAM则使用的是B+Tree结构(实际上很多存储引擎使用的是B+Tree,即每个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历),但其名字也还是叫BTREE

    我们通常所说聚集索引、覆盖索引、组合索引、前缀索引、普通索引、唯一索引等,没有特别说明,默认都是使用B+Tree结构的索引

    2.1 为什么选择B-Tree结构

    B-Tree是一种自平衡的多路查找树,其中B是Balanced (平衡)的意思,节点最大的孩子数目称为B-Tree的阶(order)。

    关于B-Tree和B+Tree,可以看看以前我的这篇文章:数据结构—多路查找树中的2-3树、2-3-4树、B树、B+树的原理详解,里面有更加详细的介绍,下面的内容都是总结自这篇文章。

    B-Tree节点也是天然有序的(排序),和平衡二叉树一样,B-Tree也能以O(logn)的时间复杂度运行进行查找、顺序读取、插入和删除的数据结构,并且所有的叶子节点都位于同一层,或者说根节点到每个叶子节点的长度都相同。

    不同的是,平衡二叉树是一般是用于优化内存中的数据的查找速度的数据结构,B-Tree一般用于优化外存中数据的查找数据的数据结构,普遍运用在数据库索引结构和文件系统。

    对硬盘中的数据结构的某个节点的访问就会发起一次磁盘IO请求,IO操作耗时远大于内存中操作的耗时。如果一棵B-Tree的阶为1001(即1个节点包含1000个关键字),高度为2,它可以储存超过10亿个关键字。那么在这棵树上,寻找某一个关键字至多需要两次硬盘的读取即可。如果采用二叉树来存储这10亿个关键字,那么会需要非常多次数的IO请求。虽然两种数据结构最终找到某个数据所需的比较次数不会少,但是使用B-Tree时,可以经历非常少的IO次数(将一大批数据IO到内存中进行比较),而IO操作时非常耗时的。

    由于B-Tree每个节点能包含比二叉树更多的数据,同时树的层级比原来的二叉树更少的特性,加上数据库充分利用了磁盘预读原理(磁盘数据存储是采用块的形式存储的,每个块的大小为4k或8k,每次IO进行数据读取时,同一个磁盘块的数据可以一次性读取出来(InnoDB存储引擎一次IO会读取的一页(默认一页16K))),因此采用B-Tree作为索引结构,能够减少定位记录时所经历IO次数,从而加快存取速度。可以说,B-Tree的数据结构就是为内外存的数据交互准备的。

    以上特点也是“为什么不选择红黑树或者其他二叉树作为外存中进行查找的数据结构”等问题的原因。

    2.2 B+Tree和B-Tree结构的区别

    B+Tree可以说是B-Tree的升级版,相对于B-Tree来说B+Tree更充分的利用了节点的空间,让查询速度更加稳定,其速度完全接近于二分法查找。

    目前大部分数据库系统及文件系统都采用 B-Tree或其变种B+Tree作为索引结构。

    B-Tree和B+Tree的主要区别在于:

    1. B-Tree的非叶节点会存储关键字(key)及其对应的数据(data),而B+Tree的非叶节点只存关键字索引(key),不会存具体的数据(data),因此B+Tree的一个节点相比B-Tree能存储更多的索引元素,一次性读入内存的需要查找的关键字(key)也就越多,存储同样的数量的数据,B+Tree的高度可以更小,相对IO读写次数就降低了。
    2. B-Tree的查询效率并不稳定,最好的情况只查询一次(根节点),最坏情况是查找到叶子节点,而B+Tree由于非叶节点不存数据,而只是叶子节点中存储有关键字的索引。所有查询都要查找到叶子节点才算命中,查询效率比较稳定。这对于sql语句的优化是非常有帮助的。
    3. B-Tree的叶子节点之间都是独立的,而B+Tree所有叶子节点形成有序链表(可以是双向链表),只需要去遍历叶子节点就可以实现整棵树的遍历。提高范围查找的效率,而B-Tree不支持这样的操作或者说效率太低。

    如下,是一颗典型的B-Tree,它的非叶节点中存储了key和data:
    在这里插入图片描述

    如下,是一颗mysql索引表中实现的一种B+Tree,它的非叶节点中只存储了key,叶子节点之间使用链表关联起来:
    在这里插入图片描述

    这里的data,不同的存储引擎有不同的实现,对于MyISAM来说,data域存放的是数据记录的地址,对于InnoDB来说,data域保存了完整的数据记录。另外,即使InnoDB和MyISAM的BTREE索引都是采用的B+Tree结构,但其具体的存储实现上,仍有很大的不同。

    磁盘中的数据页是按顺序一页一页存放的,读取的时候也都是以页为单位读取的,InnoDB引擎一页的大小通常为16K。而B+Tree结构的每一页的数据还会从按照从小到大的顺序进行排序。

    B+Tree索引结构中,非叶节点作为索引页,key专门存放索引值,data域存储的是指向另一页的指针,key索引值的大小就是data对应的页的数据中的最小索引值,即对应页最左边的数据。

    叶节点作为数据叶,key同样是索引,但data存放了对应的数据,这个数据可能是主键id,指向表数据的指针,或者是真实的表数据,两两相邻的数据页之间会采用双向链表的格式互相引用,而每一页中的数据之间则是通过单链表来连接的。

    3 MyISAM的BTREE索引实现

    MyISAM存储引擎的数据文件和索引文件是分开存储的,索引文件名为tablename. MYI,而数据文件名为tablename.MDB,并且表空间中可存储行记录数。

    在MyISAM引擎中,使用 B+Tree作为BTREE索引结构,叶节点的关键key是索引列的值,而data 则是保存数据记录的地址。

    假设有一个user表,具有id、age、name字段,其中id是主键。下图是 在MyISAM 引擎中user表的主键索引图:
    在这里插入图片描述

    假设我们要根据主键id精确查询值为12的数据,则需要进行4次磁盘IO,其中3次索引IO,一次数据IO。

    假设我们要根据主键id精确查询值为10~20的数据,则需要进行5次磁盘IO,其中4次索引IO,一次数据IO。多出来的一次索引IO是对叶子节点横向进行的范围查找,并且id的查询范围跨越了不同的数据块,如果范围在同一个数据块中,则不需要多一次的范围查找。

    MyISAM存储引擎的主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。所以在查询时即使是等值查询,也需要按照范围查询的方式在辅助索引树中检索数据。

    对于user表,假设age字段是辅助索引,那么下图是 MyISAM 引擎中user表的辅助索引图:
    在这里插入图片描述

    另外,加载到内存中的数据将可能被MyISAM缓存起来,因此不是每次的查找都会进行磁盘IO。

    4 InnoDB的BTREE索引实现

    InnoDB 也使用 B+Tree 作为BTREE索引结构。一个 InnoDB 表包含两部分,即:表结构定义和数据。在 MySQL 8.0 版本以前,表结构是存在以.frm 为后缀的文件里。而 MySQL 8.0 版本,则已经允许把表结构定义放在系统数据表中了。因为表结构定义占用的空间很小。

    MyISAM 索引文件和数据文件是分离的,索引文件仅保存数据记录的地址,而InnoDB的数据文件本身就是索引文件,数据和索引存储在一个文件t_tablename.ibd中。

    其表数据文件本身就是按 B+Tree 组织的一个索引结构,树的叶节点 data 域保存了完整的数据记录,InnoDB表数据文件本身就是主索引,索引的key是数据表的主键,这种索引就是主键索引,也称为聚集(聚簇)索引,相应的,MyISAM存储引擎的索引被称为非聚集(聚簇)索引。

    聚簇索引的一个叶子节点存储的是被查找的数据行所在的一个页(同理一个非叶子节点则是存放的一个索引页),通过B+Tree最终找到的也是一个数据页,然后数据库通过把数据页读入内存,再在内存中进行查找(数据是有序的),最后得到要查找的数据。数据页占用固定大小的空间,但是却不一定会被数据填满,因为各种原因,比如,对一个满页插入数据时会使用页分裂计数将一页分裂成两个页,造成每一个数据页有近50%的空闲空间,形成很多磁盘碎片,可能导致全表扫描变慢(数据页和索引页都有可能页分裂)。

    假设有一个user表,具有id、age、name字段,其中id是主键。下图是在InnoDB引擎中user表的主键索引图:
    在这里插入图片描述

    假设我们要根据主键id精确查询值为12的数据,则需要进行3次磁盘IO,其中3次索引IO。

    假设我们要根据主键id精确查询值为10~20的数据,则需要进行4次磁盘IO,其中4次索引IO。多出来的一次索引IO是对叶子节点横向进行的范围查找,并且id的查询范围跨越了不同的数据块,如果范围在同一个数据块中,则不需要多一次的范围查找。

    因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列(唯一索引且not null)作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含ROWID字段作为主键,这个字段长度为6个字节,类型为长整形。

    InnoDB存储引擎除了主键/聚簇索引之外的其余的索引都作为辅助索引,也称为二级索引,或者非聚集(聚簇)索引。需要注意的是,辅助索引的 data 域存储相应记录主键的值而不是地址,而MyISAM的辅助索引的data域同样存放的是数据的地址。

    假设user表的age字段作为辅助索引,下图是在InnoDB引擎中user表的辅助索引图:
    在这里插入图片描述

    在根据主索引(主键)搜索时,直接找到 key 所在的节点的data即可取出数据,而在根据辅助索引查找时,则需要先走辅助索引取出对应的主键值,然后再走一遍主索引。

    根据在辅助索引树中先获取的主键id,然后再到主键索引树检索数据的过程(即二次查询)称为回表查询。假设我们要根据age字段查询age值为18的数据,则需要进行6次磁盘IO,其中3次辅助索引IO,3次回表主索引的IO。我们在应用中应该尽量使用主键查询。

    因此,在设计表的时候,不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。也不建议使用非单调的字段作为主键,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整树的平衡,导致效率降低,而使用自增或者单调字段作为主键则是一个很好的选择(这一点对于MyISAM同样有效)。

    4.1 ROWID

    InnoDB主键策略:每个InnoDB引擎的表必须有一个“聚簇索引”,通常是通过主键索引字段构建。如果没有指定主键,则会通过第一个not null的唯一索引字段构建,如果这两个都没有,那么InnoDB会给你创建一个不可见的,长度为6个字节的row_id字段来构建。(https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html)。

    InnoDB维护了一个全局的dict_sys.row_id值,所有无主键并且没有not null的唯一键的InnoDB表,每插入一行数据,都将当前的dict_sys.row_id值作为要插入数据的row_id,然后把dict_sys.row_id的值加1。

    实际上,在代码实现时row_id是一个长度为8字节的无符号长整型(bigint unsigned)。但是,InnoDB在设计时,给row_id留的只是6个字节的长度,这样写到数据表中时只放了最后6个字节,所以row_id能写到数据表中的值,就有两个特征:

    1. row_id写入表中的值范围,是从0到2^48-1;
    2. 当row_id为2^48-1时,如果再有插入数据的行为要来申请row_id,拿到以后再取最后6个字节的话就是0。

    也就是说,写入表的row_id是从0开始到2^48-1。达到上限后,下一个值就是0,然后继续循环。

    在InnoDB逻辑里,申请到row_id=N后,就将这行数据写入表中;如果表中已经存在row_id=N的行,新写入的行就会覆盖原有的行。

    实际上,InnoDB 存储引擎为每行数据添加了三个 隐藏字段:

    1. DB_TRX_ID(6字节):表示最后一次插入或更新该行的事务 id。此外,delete 操作在内部被视为更新,只不过会在记录头Record header 中的 deleted_flag 字段将其标记为已删除。
    2. DB_ROLL_PTR(7字节) 回滚指针,指向该行的 undo log 。如果该行未被更新,则为空。
    3. DB_ROW_ID(6字节):如果没有设置主键且该表没有唯一非空索引时,InnoDB 会使用该 id
      来生成聚簇索引,这个字段值在所有的表中唯一。

    5 B-Tree索引的有效查询类型

    所谓有效查询类型,就是说发生了如下情况时,可以使用索引扫描查询,而不是全表扫描。

    1. 全值匹配:全值匹配指的是和索引列中的所有列进行匹配,例如查询name为“张三”的用户,此时就是全值匹配,对于多列索引来说,就是所有的索引列都完全匹配。
    2. 匹配最左前缀:对于多列索引可以从右向左一次匹配,后面会想详细讲解最左前缀。
    3. 匹配列前缀:可以只匹配某一列的值的开头部分,例如查询所有姓“张”的用户。对于多列索引,则只能对最左列使用列前缀匹配。
    4. 匹配范围值:由于B-Tree的有序性,可以进行基于索引列的范围匹配。所以索引可以按照升序或者降序进行扫描,以满足精确符合列顺序的ORDER BY、GROUP BY 和DISTINCT等之句的查询需求。
    5. 精确匹配和范围匹配:对于多列索引,可以对左边的列进行精准匹配,对右边的列进行范围匹配。但对于范围匹配之后的列则不能走索引,后面会详细讲解。
    6. 只访问索引的查询:B-Tree通常支持“只访问索引的查询”,即查询只需要访问索引,比如姓名列就是索引列,此时查询所有姓“张”的用户的姓名,那么我们无序访问数据行,因为索引就包含所要返回的全部结果,这是一种“覆盖索引”的优化,避免回表。

    基于这些有效查询类型,后面我们讲到索引优化部分的时候会非常有用。

    参考资料:

    1. 《 MySQL 技术内幕: InnoDB 存储引擎》
    2. 《高性能 MySQL》

    如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

    展开全文
  • Modern_B-Tree_Techniques

    2019-08-03 15:24:08
    Modern_B-Tree_Techniques
  • Analysis of B-tree data structure and its usage in computer forensicsPetra Koruga, Miroslav Bača Faculty of Organization and InformaticsUniversity of Zagreb Pavlinska 2, 42000 Varaždin, Croatia{...
  • PostgreSQL中的B-tree索引

    千次阅读 2018-10-09 10:56:06
    B-tree索引也可以用于涉及到模式匹配操作符LIKE和~ 的查询,前提是如果模式是一个常量且被固定在字符串的开头。 补充:主键和唯一键会自动创建B-tree索引,无需另外单独再为主键和唯一键创建索引。 关于B-tree索引...
  • MySQL索引原理及B-Tree / B+Tree结构详解

    万次阅读 2019-08-09 12:30:22
    MySQL索引原理及B-Tree / B+Tree结构详解 目录 摘要 数据结构及算法基础 索引的本质 B-TreeB+Tree B-Tree B+Tree 带有顺序访问指针的B+Tree 为什么使用B-TreeB+Tree) 主存存取原理 磁盘存取原理 ...
  • b treeb-tree就是一个玩意 应该很多人都看到过b树和b-树,还有b+树,不了解的小伙伴还以为这是三个东西,但是其实b树和b-树就是一种事物的两种称呼而已。 b树(Balanced Tree)多路平衡查找树 对比于二叉树来说...
  • B树(B-Tree,并不是B“减”树,横杠为连接符,容易被误导)  是一种多路搜索树(并不是二叉的):  1.定义任意非叶子结点最多只有M个儿子;且M&gt;2;  2.根结点的儿子数为[2, M];  3.除根结点以外的...
  • mysql索引原理:B-TreeB+Tree简介

    千次阅读 2019-02-16 12:06:27
    完全不了解B-Tree的读者可以先看下这篇文章: https://www.kancloud.cn/kancloud/theory-of-mysql-index/41844 B-Tree B-Tree毫无疑问是树结构,如下图: 主要有以下特性: d为大于1的一个...
  • b-tree

    2013-01-22 16:52:25
    b-tree
  • 浅析MysQL B-Tree 索引

    2021-01-19 21:38:57
    B-Tree 索引 不同的存储引擎也可能使用不同的存储结构,i如,NDB集群存储引擎内部实现使用了T-Tree结构存储这种索引,即使其名字是BTREE;InnoDB使用的是B+TreeB-Tree通常一位这所有的值都是按顺序存储的,并且...
  • B-Tree索引详解及联合索引使用

    千次阅读 2018-04-04 15:04:16
    B-Tree索引原理详解部分转载自:http://zsuil.com/?p=1184一.B-tree索引详解B-tree索引(或Balanced Tree),是一种很普遍的数据库索引结构,oracle默认的索引类型(本文也主要依据oracle来讲)。其特点是定位高效、...
  • B-tree索引:使用B-tree数据结构来存储数据(实际上一般使用的是B+tree,即每一个叶子节点都包含指向下一个叶子节点的指针,为了方便叶子节点的范围遍历)B-tree意味着所有的值都是按顺序存储的,且每一个叶子页到根...
  • 高性能Mysql:B-TREEB+-TREE

    千次阅读 2016-03-06 14:31:36
    高性能Mysql:B-TREEB+-TREE一、索引简介数据库中,索引对于查询来说至关重要。它就像书籍里的目录一样,能在磁盘页面中迅速找到所需要的记录,能够将查询性能提高好几个数量级。所以索引是应对查询性能最有效的...
  • 谷歌 B-Tree C++ 模板库

    2021-03-15 15:53:24
    谷歌开源团队近日发布了C++ B-Tree,这是一个C++模板库,实现了基于B-tree数据结构的有序内存容器。类似于STL的map、set、multimap和multiset模板,C++ B-tree也提供了btree_map、btree_set、btree_multimap和btree_...
  • The Bw-Tree: A B-tree for New HardwarePlatformsJustin J. Levandoski 1, David B. Lomet 2, Sudipta Sengupta 3Microsoft ResearchRedmond, WA 98052, USA 1 justin.levandoski@microsoft.com,2 lomet@microsoft....
  • B-treeB+tree详解(一)概念与查找

    千次阅读 2019-08-15 19:50:09
    在讲B+树之前必须先了解二叉查找树、平衡二叉树(AVLTree)和平衡多路查找树(B-Tree),B+树即由这些树逐步优化而来。 详情请查看上一篇文章(二叉查找树与平衡二叉树)! 概念 B-tree(多路搜索树,平衡多路查找...
  • 参考文章从B 树、B+ 树...B-tree 中的B代表的是Balanced , 但B-tree又不能简单扩充为balanced tree, 因为B-tree只是平衡树的一种 动态查找树主要有:二叉查找树(Binary Search Tree),平衡二叉查找树(Balanced Binar
  • 引言:大家都知道“效率”是数据库中非常重要的一个指标,如何提高效率大家可能都会想起索引,但索引又这么多种,什么场合应该使用什么索引呢?哪种索引可以提高我们的效率,哪种索引可以让我们的效率大大降低...B-...
  • 因为B+Tree非叶子节点不存储数据,而是只存储关键字和指针,所以每个节点能保存的关键字和指针比B-Tree更多,所以子节点更多,同样的数据量树的高度更矮,磁盘IO操作更少 B+Tree所有数据都在叶子节点,且叶子节点之间是...
  • b-tree.pptx

    2015-01-11 09:59:05
    有谁不知道啥是btree 就来看看吧 btree btree
  • mysql里目前只支持4种索引分别是:full-text,b-tree,hash,r-tree b-tree索引应该是mysql里最广泛的索引的了,除了archive基本所有的存储引擎都支持它.   1. full-text索引 full-text在mysql里仅有myisam支持...
  • B-tree算法需求分析及实现

    千次阅读 2018-06-28 14:07:45
    如果只是随便插入的话,经过多次插入和删除后这棵树就会退化成一张线性表,那么B-tree查找的优势也就不存在了,所以每一次插入都要使B-tree保持平衡,为了保持平衡B-tree会有以下一些特性,以一颗m阶B-tree(m叉树)为...
  • 平衡多路搜索树B树(B-tree) 二叉树,它的搜索时间复杂度为O(log2N),所以它的搜索效率和树的深度有关,如果要提高查询速度,那么就要降低树的深度。要降低树的深度,很自然的方法就是采用多叉树,再结合平衡...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 376,703
精华内容 150,681
关键字:

B-Tree