您好,欢迎来到12图资源库!分享精神,快乐你我!我们只是素材的搬运工!!
  • 首 页
  • 当前位置:首页 > 开发 > WEB开发 >
    我必须得告诉大家的MySQL优化原理(4)
    时间:2017-05-03 13:47 来源:网络整理 作者:网络 浏览:收藏 挑错 推荐 打印

    我必须得告诉大家的MySQL优化原理


    简化B+Tree

    怎么理解这两个特征?MySQL将每个节点的大小设置为一个页的整数倍(原因下文会介绍),也就是在节点空间大小一定的情况下,每个节点可以存储更多的内结点,这样每个结点能索引的范围更大更精确。所有的叶子节点使用指针链接的好处是可以进行区间访问,比如上图中,如果查找大于20而小于30的记录,只需要找到节点20,就可以遍历指针依次找到25、30。如果没有链接指针的话,就无法进行区间查找。这也是MySQL使用 B+Tree 作为索引存储结构的重要原因。

    MySQL为何将节点大小设置为页的整数倍,这就需要理解磁盘的存储原理。磁盘本身存取就比主存慢很多,在加上机械运动损耗(特别是普通的机械硬盘),磁盘的存取速度往往是主存的几百万分之一,为了尽量减少磁盘I/O,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存,预读的长度一般为页的整数倍。

    页是计算机管理存储器的逻辑块,硬件及OS往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(许多OS中,页的大小通常为4K)。主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。

    MySQL巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的,每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了读取一个节点只需一次I/O。假设 B+Tree 的高度为h,一次检索最多需要 h-1 I/O(根节点常驻内存),复杂度$O(h) = O(\log_{M}N)$。实际应用场景中,M通常较大,常常超过100,因此树的高度一般都比较小,通常不超过3。

    最后简单了解下 B+Tree 节点的操作,在整体上对索引的维护有一个大概的了解,虽然索引可以大大提高查询效率,但维护索引仍要花费很大的代价,因此合理的创建索引也就尤为重要。

    仍以上面的树为例,我们假设每个节点只能存储4个内节点。首先要插入第一个节点28,如下图所示。

    我必须得告诉大家的MySQL优化原理


    leaf page和index page都没有满

    接着插入下一个节点70,在Index Page中查询后得知应该插入到50 – 70之间的叶子节点,但叶子节点已满,这时候就需要进行也分裂的操作,当前的叶子节点起点为50,所以根据中间值来拆分叶子节点,如下图所示。

    我必须得告诉大家的MySQL优化原理


    Leaf Page拆分

    最后插入一个节点95,这时候Index Page和Leaf Page都满了,就需要做两次拆分,如下图所示。

    我必须得告诉大家的MySQL优化原理


    Leaf Page与Index Page拆分

    拆分后最终形成了这样一颗树。

    我必须得告诉大家的MySQL优化原理


    最终树

    B+Tree 为了保持平衡,对于新插入的值需要做大量的拆分页操作,而页的拆分需要I/O操作,为了尽可能的减少页的拆分操作, B+Tree 也提供了类似于平衡二叉树的旋转功能。当Leaf Page已满但其左右兄弟节点没有满的情况下, B+Tree 并不急于去做拆分操作,而是将记录移到当前所在页的兄弟节点上。通常情况下,左兄弟会被先检查用来做旋转操作。就比如上面第二个示例,当插入70的时候,并不会去做页拆分,而是左旋操作。

    我必须得告诉大家的MySQL优化原理


    左旋操作

    通过旋转操作可以最大限度的减少页分裂,从而减少索引维护过程中的磁盘的I/O操作,也提高索引维护效率。需要注意的是,删除节点跟插入节点类型,仍然需要旋转和拆分操作,这里就不再说明。

    高性能策略

    通过上文,相信你对 B+Tree 的数据结构已经有了大致的了解,但MySQL中索引是如何组织数据的存储呢?以一个简单的示例来说明,假如有如下数据表:

    CREATE TABLE People(  

    last_name varchar(50) not null 

    first_name varchar(50) not null 

    dob date not null 

    gender enum(`m`,`f`) not null 

    key(last_name,first_name,dob)  

    ); 

    对于表中每一行数据,索引中包含了last_name、first_name、dob列的值,下图展示了索引是如何组织数据存储的。

    (责任编辑:admin)