B树和MySql索引

1.什么是B树

它是一种平衡得多叉树,称为B树,一颗M阶的B树,是一颗平衡的M路的多叉树,可以是空树或者满足一下性质:

  • 根节点至少有两个孩子。
  • 每个节点都包含k-1个关键字和K个孩子,其中 ceil(m/2) ≤ k ≤ m ceil是向上取整函数。
  • 每个叶子节点都包含k-1个关键字,其中 ceil(m/2) ≤ k ≤ m。
  • 所有的叶子结点都在同一层,
  • 所有的节点中的关键字都是从小到大排序,节点当中k-1个元素正好是K个孩子所包含的元素的值域划分。
  • 每个结点的结构为:(n,A0,K1,A1,K2,A2,… ,Kn,An)其中,Ki(1≤i≤n)为关键字,且Ki<Ki+1(1≤i≤n-1)。Ai(0≤i≤n)为指向子树根结点的指针。且Ai所指子树所有结点中的关键字均小于Ki+1。n为结点中关键字的个数,满足ceil(m/2)-1≤n≤m-1。

2.B树的插入分析

为了简单起见,假设M = 3. 即三叉树,每个节点中存储两个数据,两个数据可以将区间分割成三
个部分,因此节点应该有三个孩子,为了后续实现简单期间,节点的结构如下:

注意:孩子比数据多一个

3.B树插入的简单实现

#pragma once
#include <iostream>
#include <vector>
using namespace std;

template<class K, size_t M = 3>
class TreeNode
{
public:
	TreeNode()
	{
		for (int i = 0; i < M; i++)
		{
			_subs[i] = nullptr;
			_key[i] = K();
		}
		_subs[M] = nullptr;
		_parent = nullptr;
		_n = 0;
	}
public:
	K _key[M];
	TreeNode<K, M>* _subs[M + 1];
	TreeNode<K, M>* _parent;
	size_t _n;//洢
};

template<class K, size_t M=3>
class BTree
{
	typedef TreeNode<K, M> Node;
public:
	pair<Node*, int> Find(const K& key)
	{
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			size_t i = 0;
			for (i = 0; i < cur->_n; i++)
			{
				if (cur->_key[i] == key)
				{
					return make_pair(cur, i);
				}
				else if (cur->_key[i] > key)
					break;
			}
			parent = cur;
			cur = cur->_subs[i];
		}
		return make_pair(parent, -1);
	}
	void InsertKey(Node* parent, Node* child, const K& key)
	{
		size_t end = parent->_n;
		while (end >= 0)
		{
			if (parent->_key[end] > key)
			{
				parent->_key[end + 1] = parent->_key[end];
				parent->_subs[end + 2] = parent->_subs[end + 1];
				--end;
			}
			else break;
		}
		parent->_key[end + 1] = key;
		parent->_subs[end + 2] = child;
		if (child) child->_parent = parent;
		parent->_n++;
	}

	bool Insert(const K& key)
	{
		pair<Node*, int> ret = Find(key);
		if (ret.second != -1) return false;
		if (_root == nullptr)
		{
			_root = new Node();
			_root->_key[0] = key;
			_root->_n++;
			return true;
		}

		Node* parent = ret.first;
		Node* child = nullptr;
		K newkey = key;
		while (1)
		{
			InsertKey(parent, child, newkey);
			if (parent->_n < M) return true;
			else
			{
				size_t mid = parent->_n / 2;
				
				Node* brother = new Node();
				size_t j = 0;
				size_t i = mid + 1;
				for (i; i < M; i++)
				{
					
					brother->_subs[j] = parent->_subs[i];
					brother->_key[j] = parent->_key[j];
					if (parent->_subs[i])
					{
						parent->_subs[i]->_parent = brother;
					}
					j++;
					parent->_key[i] = K();
					parent->_subs[i] = nullptr;
				}
				  
				brother->_subs[j] = parent->_subs[i];
				if (parent->_subs[i])
				{
					parent->_subs[i]->_parent = brother;
				}

				parent->_subs[i] = nullptr;
				
				brother->_n = j;
				parent->_n -= (brother->_n + 1);

				K midKey = parent->_key[mid];
				parent->_key[mid] = K();


				if (parent->_parent == nullptr)
				{
					_root = new Node();
					_root->_key[0] = midKey;
					_root->_subs[0] = parent;
					_root->_subs[0] = brother;
					_root->_n = 1;

					parent->_parent = _root;
					brother->_parent = _root;
					break;
				}
				else
				{
					
					newkey = midKey;
					child = brother;
					parent = parent->_parent;
				}

			}
		}
		return true;
	}
	void _Inorder(Node* cur)
	{
		if (cur == nullptr)
			return;

		
		size_t i = 0;
		for (; i < cur->_n; ++i)
		{
			_Inorder(cur->_subs[i]);    
			cout << cur->_key[i] << " "; 
		}

		_Inorder(cur->_subs[i]);   
	}

	void Inorder()
	{
		_Inorder(_root);
	}

private:
	Node* _root = nullptr;
};

4.B树,B+树B*树三者的关系

B+树是B树的变形,是在B树基础上优化的多路平衡搜索树,B+树的规则跟B树基本类似,但是又
在B树的基础上做了以下几点改进优化:

分支节点的子树指针与关键字个数相同
分支节点的子树指针p[i]指向关键字值大小在[k[i],k[i+1])区间之间
所有叶子节点增加一个链接指针链接在一起
所有关键字及其映射数据都在叶子节点出现

B+树的特性:
1. 所有关键字都出现在叶子节点的链表中,且链表中的节点都是有序的。
2. 不可能在分支节点中命中。
3. 分支节点相当于是叶子节点的索引,叶子节点才是存储数据的数据层。

B*树是B+树的变形,在B+树的非根和非叶子节点再增加指向兄弟节点的指针。
B+。

B+树的分裂:
当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增
加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向
兄弟的指针。

B*树的分裂:
当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结
点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如
果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父
结点增加新结点的指针。
所以,B*树分配新结点的概率比B+树要低,空间使用率更高;

通过以上介绍,大致将B树,B+树,B*树总结如下:
B树:有序数组+平衡多叉树;
B+树:有序数组链表+平衡多叉树;
B*树:一棵更丰满的,空间利用率更高的B+树。

4.什么是索引

B-树最常见的应用就是用来做索引。索引通俗的说就是为了方便用户快速找到所寻之物,比如:
书籍目录可以让读者快速找到相关信息,hao123网页导航网站,为了让用户能够快速的找到有价
值的分类网站,本质上就是互联网页面中的索引结构。
MySQL官方对索引的定义为:索引(index)是帮助MySQL高效获取数据的数据结构,简单来说:
索引就是数据结构。
当数据量很大时,为了能够方便管理数据,提高数据查询的效率,一般都会选择将数据保存到数
据库,因此数据库不仅仅是帮助用户管理数据,而且数据库系统还维护着满足特定查找算法的数
据结构,这些数据结构以某种方式引用数据,这样就可以在这些数据结构上实现高级查找算法,
该数据结构就是索引。

索引是基于表的,而不是基于数据库的。

5.B树和索引的关系

5.1 MyISAM

MyISAM引擎是MySQL5.5.8版本之前默认的存储引擎,不支持事物,支持全文检索,使用B+Tree
作为索引结构,叶节点的data域存放的是数据记录的地址,其结构如下:

上图是以以Col1为主键,MyISAM的示意图,可以看出MyISAM的索引文件仅仅保存数据记录的
地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索
引要求key是唯一的,而辅助索引的key可以重复。如果想在Col2上建立一个辅助索引,则此索引
的结构如下图所示:

同样也是一棵B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按
照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为
地址,读取相应数据记录。MyISAM的索引方式也叫做“非聚集索引”的。

5.2 InnoDB

InnoDB存储引擎支持事务,其设计目标主要面向在线事务处理的应用,从MySQL数据库5.5.8版
本开始,InnoDB存储引擎是默认的存储引擎。InnoDB支持B+树索引、全文索引、哈希索引。但
InnoDB使用B+Tree作为索引结构时,具体实现方式却与MyISAM截然不同。
第一个区别是InnoDB的数据文件本身就是索引文件。MyISAM索引文件和数据文件是分离的,
索引文件仅保存数据记录的地址。而InnoDB索引,表数据文件本身就是按B+Tree组织的一个索
引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此
InnoDB表数据文件本身就是主索引。

上图是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录,
这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有
主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数
据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主
键,这个字段长度为6个字节,类型为长整型。
第二个区别是InnoDB的辅助索引data域存储相应记录主键的值而不是地址,所有辅助索引都引用
主键作为data域。

聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先
检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

函数指针

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值