数据库索引的底层原理

二叉树:
    二叉树是每个节点最多有两个子树的树结构,常用于实现二叉查找树和二叉堆。它有如下特性:
1.每个节点包含一个元素和n个节点,0≤n≤2
2.左子树和右子树是有顺序的,次序不能任意颠倒。左子树的值要小于父节点,右子树的值要大于父节点
 
平衡二叉树:
    满足二叉树所有特性,新增一个特性:
左右两个子树的高度差的绝对值不超过1,并且左右子树都是一颗平衡二叉树。
 
注意:当左右子树的高度差的绝对值超过1时,将当前树左旋以保持平衡,来满足平衡二叉树的特性;平衡二叉树的查找效率和二分法查找是一样
        一颗平衡二叉树能容纳多少的结点呢?这跟树的高度是有关系的,假设树的高度为h,那每一层最多容纳的结点数量为2^(n-1),整棵树最多容纳节点数为2^0+2^1+2^2+...+2^(h-1)。这样计算,100w数据树的高度大概在20左右,那也就是说从有着100w条数据的平衡二叉树中找一个数据,最坏的情况下需要20次查找。如果是内存操作,效率也是很高的!但是我们数据库中的数据基本都是放在磁盘中的,每读取一个二叉树的结点就是一次磁盘IO,这样我们找一条数据如果要经过20次磁盘的IO?那性能就成了一个很大的问题了!那我们是不是可以把这棵树压缩一下,让每一层能够容纳更多的节点呢?虽然我矮,但是我胖啊...
 
B- Tree(B树  注意:中间是杠精的杠而不是减,所以也不要读成B减Tree了)
    一颗m阶的B树有如下特性:
    1.每个节点最多m个子节点
    2.除了根节点和叶子节点外,每个节点最少有m/2(向上取整)个子节点
    3.如果根节点不是叶子节点,那根节点至少包含两个子节点
    4.所有的叶子节点都位于同一层
    5.每个节点都包含k个元素(关键字),这里m/2≤k<m,这里m/2向下取整
    6.每个节点中的元素(关键字)从小到大排列
    7.每个元素(关键字)左节点的值都小于等于该元素;右节点的值都大于等于改元素
 
在B-Tree中,每个结点都可能包含多个元素,并且非叶子结点在元素的左右都有指向子结点的指针;B-Tree的查询效率好像也并不比平衡二叉树高。但是查询所经过的结点数量要少很多,也就意味着要少很多次的磁盘IO,这对性能的提升是很大的。
 
 
 
B+Tree:
    B+Tree是在B-Tree的基础上的一种优化,使其更适合实现外存储索引结构。B+Tree与B-Tree的结构很像,但有自己的几个特性:
    1.所有的非叶子节点只存储关键字信息
    2.所有的卫星数据(具体数据)都存储在叶子节点中
    3.所有的叶子节点中包含了全部元素的信息
    4.所有的叶子结点之间都有一个链指针
 
 
B-Tree Or B+Tree?
    在讲这两种数据结构在数据库中的选择之前,我们还需要了解到一个知识点是操作系统从磁盘读取数据到内存是以磁盘块(block)为基本单位的,位于同一个磁盘块的数据会被一次性读取出来,而不是需要什么取什么。
即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理: 当一个数据被用到时,其附近的数据也通常会马上被使用。
    预读的长度一般为页(page)的整数倍。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页的大小通常为4k)。
B-Tree和B+Tree该如何选择呢?都有哪些优劣呢?如下:
    1.B-Tree非叶子节点也保存具体数据,所以查到某个关键字就可以返回数据。而B+Tree所有的数据都在叶子节点,每次查找都得到叶子节点。所以同样高度的B-Tree和B+Tree中,B-Tree查找某个关键字节点的效率更高。
    2.由于B+Tree所有的数据都在叶子节点上面,且叶子节点之间有指针连接;所以在找大于某个值或者小于某个关键字的数据时,B+Tree只需要找到该关键字,然后沿着链表遍历就可以了,而B-Tree还需要遍历该关键字节点的根节点去搜索。
    3.  由于B-Tree的每个节点(这里的节点可以理解为一个数据页)都存储主键+实际数据,而B+Tree非叶子节点只存储关键字信息,而每个页大小是有限的,所以同一页能存储的B-Tree数据会比B+Tree存储的少。这样同样总量的数据,B-Tree的深度会更大,增大查询时的磁盘I/O次数,进而影响查询效率。
鉴于以上的比较,所以在常用的关系型数据库中,都会选择B+Tree的数据结构来存储数据。
 
innodb引擎数据存储:
    1.每个页的大小为16k
    2.在innodb中根结点是会预读到内存中的,所以结点的物理地址固定会比较好
    3.新的数据放入新创建的页,不移动原有页面的任何记录
每次新增数据,都是将一个页写满,然后新创建一个页继续写,这里其实是有个隐含条件的,那就是主键自增!主键自增写入时新插入的数据不会影响到原有页,插入效率高!且页的利用率高!但是如果主键是无序的或者随机的,那每次的插入可能会导致原有页频繁的分裂,影响插入效率!降低页的利用率!这也是为什么在innodb中建议设置主键自增的原因!
    有数据插入那就有删除,如果这个用户表频繁的插入和删除,那会导致数据页产生碎片,页的空间利用率低,还会导致树变的“虚高”,降低查询效率!这可以通过索引重建来消除碎片提高查询效率!
重建索引命令:REPAIR TABLE tbl_name QUICK;
 
非聚集索引的存储结构与前面是一样的,不同的是在叶子结点的数据部分存的不再是具体的数据,而数据的聚集索引的key。所以通过非聚集索引查找的过程是先找到该索引key对应的聚集索引的key,然后再拿聚集索引的key到主键索引树上查找对应的数据,这个过程称为回表!
 
MyISAM引擎:
    1、主键索引树的叶子节点的数据区域没有存放实际的数据,存放的是数据记录的地址。
    2、数据的存储不是按主键顺序存放的,按写入的顺序存放。
 
innodb引擎数据在物理上是按主键顺序存放;
MyISAM引擎数据在物理上按插入的顺序存放;
posted @ 2019-10-21 11:41  兔老霸夏  阅读(467)  评论(0编辑  收藏  举报