谈到索引,你们并不陌生。索引自己是一种数据结构,存在的目的主要是为了缩短数据检索的时间,最大程度减小磁盘 IO。数据库
任何有数据的场景几乎都有索引,好比手机通信录、文件系统(ext4xfsntfs)、数据库系统(MySQLOracle)。数据库系统和文件系统通常都采用 B+ 树来存储索引信息,B+ 树兼顾写和读的性能,最极端时检索复杂度为 O(logN),其中 N 指的是节点数量,logN 表示对磁盘 IO 扫描的总次数。数据结构
MySQL 支持的索引结构有四种:B+ 树,R 树,HASH,FULLTEXT。性能
本篇简单介绍下 B+ 树,下一篇讲 MySQL 经常使用的两种引擎 MyISAM 和 InnoDB 的 B+ 树索引实现,其他的后面会讲到。优化
再讲什么是 B+ 树以前,先来了看下什么是二叉树。spa
树自己是一种数据存储结构,由于相似现实生活中的树而命名。3d
一个看似没有修剪过的树,其实这是一棵二叉树,每一个节点最多有两个子节点。指针
树相关的基础概念:blog
拿图 1 这棵树举例说明:排序
平衡因子:某节点的左子树与右子树深度的差值,通常结果为绝对值。索引
图 1 是一颗很是普通的树,很是容易退化为一张链表。若是把图 1 换成以下图, 根节点就变为 4,6 退化为 4 的儿子节点,这棵树就退化为一张链表。
链表的查找很是慢,只能按照节点顺序查找,每一个节点都遍历一遍,时间复杂度为 O(n),没法随机查找。
那对图 1 进行下改造,把数据从新节点从新链接下,图 2 以下:
图 2 能够看到如下特性:
1. 全部左子树的节点都小于其对应的父节点(4,5,6)<(7);(4)<(5);(8)< (9);
2. 全部右子树上的节点都大于其对应的父节点(8,9,10)>(7);(6)>(5);(10)>(9);
3. 每一个节点的平衡因子差值绝对值 <=1;
4. 每一个节点都符合以上三个特征。
知足这样条件的树叫平衡二叉树(AVL)树。
问:那再次查找节点 5,须要遍历多少次呢?
因为数据是按照顺序组织的,那查找起来很是快,从上往下找:7-5,只须要在左子树上查找,也就是遍历 2 次就找到了 5。假设要找到叶子节点 10,只须要在右子树上查找,那也最多须要 3 次,7-9-10。也就说 AVL 树在查找方面性能很好,最坏的状况是找到一个节点须要消耗的次数也就是树的层数, 复杂度为 O(logN)
若是节点很是多呢?假设如今有 31 个节点,用 AVL 树表示如图 3:
图 3 是一棵高度为 4 的 AVL 树,有 5 层共 31 个节点,橙色是 ROOT 节点,蓝色是叶子节点。对 AVL 树的查找来看起来已经很完美了,能不能再优化下?好比,可否把这个节点里存放的 KEY 增长?可否减小树的总层数?那减小纵深只能从横向来想办法,这时候能够考虑用多叉树。
B 树是一种多叉的 AVL 树。B-Tree 减小了 AVL 数的高度,增长了每一个节点的 KEY 数量。
B 树的特性:(m 为阶数:结点的孩子个数最大值)
1. 树中每一个节点最多含有 m 个孩子节点 (m>=2);
2. 除根节点和叶子结点外,其余节点的孩子数量 >=ceil(m / 2);
3. 若根节点不是叶子结点,最少有两个孩子
4. 每一个非叶子结点中包含有 n 个关键字信息:(n,P0,K1,P1,K2,P2,......,Kn,Pn) 其中:
按照这个要求,把图 3 简单变为一棵 B 树,见图 4:
图 4 是一棵 4 阶 B 树,总共有 11 个节点,节点数比图 3 少了 20 个;层数为 3,比图 3 少了两层。实际应用中,每一个最小单元不是 KEY,而通常是按照块(BLOCK)来算。好比磁盘文件系统 EXT4 每块 4KB;数据库好比 PostgreSQL 是 8KB,MySQL InnoDB 是 16KB, MySQL NDB 是 32KB 等。
因此再次理清图 4 的 B 树,变为图 5:
图 5 每一个节点的基本单元是一个磁盘块(BLOCK,默认 4KB),根节点含有一个键值,其余节点含有 3 个键值,每一个磁盘块包含对应的键值与数据。
好比如今要读取 KEY 为 31 的记录:先找到根节点磁盘块(1),读入内存。(第一次 IO);关键字 31 大于区间(16,),根据指针 P2 找到磁盘块 3,读入内存(第二次 IO);31 大于区间(20,24,28),根据指针 P4 读取磁盘块 11(第三次 IO),在磁盘块 11 中找到 KEY 为 31 的记录,返回结果。这期间有三次磁盘 IO 的读取。能够明确看到,B 树相对于 AVL 树,减小了树的节点数与树的深度,减小了磁盘 IO。
看到这里其实有一个问题,三次 IO,前两次 IO 其实从磁盘读取了没必要要的数据,由于只用比较 KEY,因此非叶子节点对应的 DATA 彻底没有必要,若是 DATA 很大,那彻底是浪费内存资源。考虑下可否把非叶子节点的 DATA 拿掉?
B+ 树是对 B 树的一个小升级。大部分数据库的索引都是基于 B+ 树存储的。MySQL 的 MyISAM 和 InnoDB 引擎的索引都是基于 B+ 树存储。
B+ 树最大的几个特色:
1. 非叶子节点只保留 KEY,放弃 DATA;
2. KEY 和 DATA一块儿,在叶子节点,而且保存为一个有序链表(正序,反序,或者双向);
3. B+ 树的查找与 B 树不一样,当某个结点的 KEY 与所查的 KEY 相等时,并不中止查找,而是沿着这个 KEY 左边的指针向下,一直查到该关键字所在的叶子结点为止。
那对图 5 的 B 树作一个调整,变为如下 B+ 树,见图 6:
图 6 是一棵 6 阶 B+ 树。不一样于图 5,非叶子节点再也不包含除了主键外的数据,数据所有放在叶子节点,而且全部叶子节点存放在一个单向链表里,固然也能够双向链表。能够看到,B+ 树同时具备平衡多叉树和链表的优势,便可兼顾 B 树对范围查找的高效,又可兼顾链表随机写入的高效, 这也是大部分数据库都用 B+ 树来存储索引的缘由。
本篇是为了下一篇介绍 MySQL 的两种经常使用引擎:MyISAM 和 InnoDB 索引结构作了一个铺垫,下期见。
关于 MySQL 的技术内容,大家还有什么想知道的吗?赶忙留言告诉小编吧!