可以说数据库必须有索引,没有索引则检索过程变成了顺序查找,O(n)的时间复杂度几乎是不能忍受的。我们非常容易想象出一个只有单关键字组成的表如何使用B+树进行索引,只要将关键字存储到树的节点即可。当数据库一条记录里包含多个字段时,一棵B+树就只能存储主键,如果检索的是非主键字段,则主键索引失去作用,又变成顺序查找了。这时应该在第二个要检索的列上建立第二套索引。  这个索引由独立的B+树来组织。有两种常见的方法可以解决多个B+树访问同一套表数据的问题,一种叫做聚簇索引(clustered index ),一种叫做非聚簇索引(secondary index)。这两个名字虽然都叫做索引,但这并不是一种单独的索引类型,而是一种数据存储方式。对于聚簇索引存储来说,行数据和主键B+树存储在一起,辅助键B+树只存储辅助键和主键,主键和非主键B+树几乎是两种类型的树。对于非聚簇索引存储来说,主键B+树在叶子节点存储指向真正数据行的指针,而非主键。

  InnoDB使用的是聚簇索引,将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14"这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。若对Name列进行条件搜索,则需要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树种再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。

  MyISM使用的是非聚簇索引,非聚簇索引的两棵B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键检索无需访问主键的索引树。

  为了更形象说明这两种索引的区别,我们假想一个表如下图存储了4行数据。其中Id作为主索引,Name作为辅助索引。图示清晰的显示了聚簇索引和非聚簇索引的差异。

  我们重点关注聚簇索引,看上去聚簇索引的效率明显要低于非聚簇索引,因为每次使用辅助索引检索都要经过两次B+树查找,这不是多此一举吗?聚簇索引的优势在哪?

  1 由于行数据和叶子节点存储在一起,这样主键和行数据是一起被载入内存的,找到叶子节点就可以立刻将行数据返回了,如果按照主键Id来组织数据,获得数据更快。

  2 辅助索引使用主键作为"指针" 而不是使用地址值作为指针的好处是,减少了当出现行移动或者数据页分裂时辅助索引的维护工作,使用主键值当作指针会让辅助索引占用更多的空间,换来的好处是InnoDB在移动行时无须更新辅助索引中的这个"指针"。也就是说行的位置(实现中通过16K的Page来定位,后面会涉及)会随着数据库里数据的修改而发生变化(前面的B+树节点分裂以及Page的分裂),使用聚簇索引就可以保证不管这个主键B+树的节点如何变化,辅助索引树都不受影响。

  3 Page结构

  如果说前面的内容偏向于解释原理,那后面就开始涉及具体实现了。

  理解InnoDB的实现不得不提Page结构,Page是整个InnoDB存储的最基本构件,也是InnoDB磁盘管理的最小单位,与数据库相关的所有内容都存储在这种Page结构里。Page分为几种类型,常见的页类型有数据页(B-tree Node)Undo页(Undo Log Page)系统页(System Page) 事务数据页(Transaction System Page)等。单个Page的大小是16K(编译宏UNIV_PAGE_SIZE控制),每个Page使用一个32位的int值来唯一标识,这也正好对应InnoDB最大64TB的存储容量(16Kib * 2^32 = 64Tib)。一个Page的基本结构如下图所示:

  每个Page都有通用的头和尾,但是中部的内容根据Page的类型不同而发生变化。Page的头部里有我们关心的一些数据,下图把Page的头部详细信息显示出来:

  我们重点关注和数据组织结构相关的字段:Page的头部保存了两个指针,分别指向前一个Page和后一个Page,头部还有Page的类型信息和用来唯一标识Page的编号。根据这两个指针我们很容易想象出Page链接起来就是一个双向链表的结构。

  再看看Page的主体内容,我们主要关注行数据和索引的存储,他们都位于Page的User Records部分,User Records占据Page的大部分空间,User Records由一条一条的Record组成,每条记录代表索引树上的一个节点(非叶子节点和叶子节点)。在一个Page内部,单链表的头尾由固定内容的两条记录来表示,字符串形式的"Infimum"代表开头,"Supremum"代表结尾。这两个用来代表开头结尾的Record存储在System Records的段里,这个System Records和User Records是两个平行的段。InnoDB存在4种不同的Record,它们分别是1主键索引树非叶节点 2主键索引树叶子节点 3辅助键索引树非叶节点 4辅助键索引树叶子节点。这4种节点的Record格式有一些差异,但是它们都存储着Next指针指向下一个Record。后续我们会详细介绍这4种节点,现在只需要把Record当成一个存储了数据同时含有Next指针的单链表节点即可。

  User Record在Page内以单链表的形式存在,最初数据是按照插入的先后顺序排列的,但是随着新数据的插入和旧数据的删除,数据物理顺序会变得混乱,但他们依然保持着逻辑上的先后顺序。

  把User Record的组织形式和若干Page组合起来,就看到了稍微完整的形式。

  现在看下如何定位一个Record:

  1 通过根节点开始遍历一个索引的B+树,通过各层非叶子节点最终到达一个Page,这个Page里存放的都是叶子节点。

  2 在Page内从"Infimum"节点开始遍历单链表(这种遍历往往会被优化),如果找到该键则成功返回。如果记录到达了"supremum",说明当前Page里没有合适的键,这时要借助Page的Next Page指针,跳转到下一个Page继续从"Infimum"开始逐个查找。

  详细看下不同类型的Record里到底存储了什么数据,根据B+树节点的不同,User Record可以被分成四种格式,下图种按照颜色予以区分。

  1 主索引树非叶节点(绿色)

  1 子节点存储的主键里最小的值(Min Cluster Key on Child),这是B+树必须的,作用是在一个Page里定位到具体的记录的位置。

  2 最小的值所在的Page的编号(Child Page Number),作用是定位Record。

  2 主索引树叶子节点(黄色)

  1 主键(Cluster Key Fields),B+树必须的,也是数据行的一部分

  2 除去主键以外的所有列(Non-Key Fields),这是数据行的除去主键的其他所有列的集合。

  这里的1和2两部分加起来就是一个完整的数据行。

  3 辅助索引树非叶节点非(蓝色)

  1 子节点里存储的辅助键值里的最小的值(Min Secondary-Key on Child),这是B+树必须的,作用是在一个Page里定位到具体的记录的位置。

  2 主键值(Cluster Key Fields),非叶子节点为什么要存储主键呢?因为辅助索引是可以不唯一的,但是B+树要求键的值必须唯一,所以这里把辅助键的值和主键的值合并起来作为在B+树中的真正键值,保证了唯一性。但是这也导致在辅助索引B+树中非叶节点反而比叶子节点多了4个字节。(即下图中蓝色节点反而比红色多了4字节)

  3 最小的值所在的Page的编号(Child Page Number),作用是定位Record。

  4 辅助索引树叶子节点(红色)

  1 辅助索引键值(Secondary Key Fields),这是B+树必须的。

  2 主键值(Cluster Key Fields),用来在主索引树里再做一次B+树检索来找到整条记录。

  下面是本篇最重要的部分了,结合B+树的结构和前面介绍的4种Record的内容,我们终于可以画出一幅全景图。由于辅助索引的B+树与主键索引有相似的结构,这里只画出了主键索引树的结构图,只包含了"主键非叶节点"和"主键叶子节点"两种节点,也就是上图的的绿色和黄色的部分。

  把上图还原成下面这个更简洁的树形示意图,这就是B+树的一部分。注意Page和B+树节点之间并没有一一对应的关系,Page只是作为一个Record的保存容器,它存在的目的是便于对磁盘空间进行批量管理,上图中的编号为47的Page在树形结构上就被拆分成了两个独立节点。

  至此本篇就算结束了,本篇只是对InnoDB索引相关的数据结构和实现进行了一些梳理总结,并未涉及到Mysql的实战经验。这主要是基于几点原因:

  1 原理是基石,只有充分了解InnoDB索引的工作方式,我们才有能力高效的使用好它。

  2 原理性知识特别适合使用图示,我个人非常喜欢这种表达方式。

  3 关于InnoDB优化,在《高性能Mysql》里有更加全面的介绍,对优化Mysql感兴趣的同学完全可以自己获取相关知识,我自己的积累还未达到能分享这些内容的地步。

  另:对InnoDB实现有更多兴趣的同学可以看看Jeremy Cole的博客(参考文献三篇文章的来源),这位老兄曾先后在Mysql,Yahoo,Twitter,Google从事数据库相关工作,他的文章非常棒!

Mysql的存储引擎和索引的更多相关文章

  1. mysql存储引擎和索引

    正确的创建合适的索引,是提升数据库查询性能的基础. 第一章 mysql之索引 索引的定义:索引是为了加速对表中数据行的检索而创建的一种分散存储的数据结构. 我们为什么要使用索引: a.极大的减少存储引 ...

  2. Mysql 版本号、存储引擎、索引查询

    [1]Mysql 版本号.存储引擎.索引查询 # 查看数据库版本号 SELECT VERSION(); # 查看数据库支持的引擎(默认即Support == DEFAULT行) SHOW ENGINE ...

  3. mysql 存储引擎对索引的支持

    一.首先给出mysql官方文档给出的不同存储引擎对索引的支持 从上面的图中可以得知,mysql 是支持hash索引的,但支持和不支持又和具体的存储引擎有关系.从图中看到InnoDB是支持Btree索引 ...

  4. MySQL InnoDB存储引擎体系架构 —— 索引高级

    转载地址:https://mp.weixin.qq.com/s/HNnzAgUtBoDhhJpsA0fjKQ 世界上只两件东西能震撼人们的心灵:一件是我们心中崇高的道德标准:另一件是我们头顶上灿烂的星 ...

  5. 【MySQL】MySQL(四)存储引擎、索引、锁、集群

    MySQL存储引擎 MySQL体系结构 体系结构的概念 任何一套系统当中,每个部件都能起到一定的作用! MySQL的体系结构 体系结构详解 客户端连接 支持接口:支持的客户端连接,例如C.Java.P ...

  6. MySQL存储引擎,索引及基本优化策略

    存储引擎 与Oracle, SQL Server这些数据库不同,MySQL提供了多种存储引擎.什么是存储引擎?存储引擎其实就是一套对于数据如何存储,查询,更新,建立索引等接口的实现.不同存储引擎特性有 ...

  7. MYSQL的存储引擎一般只要哪些?

    根据个人个人见解: MySQL的存储引擎(构成.安全.锁) Myisam:数据操作快速的一种引擎,支持全文检索.文件保存在数据库名称为目录名的 目录中,有3个文件,分别是表定义文件(.frm).数据文 ...

  8. MySQL常用存储引擎及如何选择

    一.MySQL的存储引擎 完整的引擎说明还是看官方文档:http://dev.mysql.com/doc/refman/5.6/en/storage-engines.html 这里介绍一些主要的引擎 ...

  9. mysql 的存储引擎介绍

    在数据库中存的就是一张张有着千丝万缕关系的表,所以表设计的好坏,将直接影响着整个数据库.而在设计表的时候,我们都会关注一个问题,使用什么存储引擎.等一下,存储引擎?什么是存储引擎? 什么是存储引擎? ...

随机推荐

  1. 用C语言显示汉字的演示程序

    汉字是方块字,宽高相等的汉字库在嵌入式领域有着广泛的应用,且其解析也相对来说是比较简单的.汉字在汉字库中的索引一般会遵循GB2312/GBK编码规则,GB2312/GBK规定汉字编码由2个字节组成,其 ...

  2. centos查看启动时间

    系统启动时间 who -b date -d "$(awk -F. '{print $1}' /proc/uptime) second ago" +"%Y-%m-%d %H ...

  3. C# 使用SkinSharp皮肤库

    SkinSharp是Windows环境下一款强大的通用换肤库. SkinSharp作为通用换肤库,只需要在您的程序中添加一行代码,就能让您的界面焕然一新,并拥有多种主题风格和色调的动态切换功能以及Ae ...

  4. 函数preg_replace()与str_replace()

    如截图,preg_replace()的用法 <?php $str="as2223adfsf0s4df0sdfsdf"; echo preg_replace("/0/ ...

  5. UVA - 11920 0 s, 1 s and ? Marks

    Description  0 s, 1 s and ? Marks  Given a string consisting of 0, 1 and ? only, change all the ? to ...

  6. fstrict-aliasing

    承如“optimization blocks”文中所述,由于相同的指针可能指向相关的内存区,因此编译器将不做过分的优化…… 特意搜了下编译器在不同的优化等级下都有哪些默认优化,因此有了此记录(比较长, ...

  7. keepalived双BACKUP加nopreempt失效、手动监控服务脚步。

    keepalived双BACKUP加nopreempt不起作用,两个机器同时拥有vip, 排查几天发现是防火墙问题,啃爹. 打开  vi /etc/sysconfig/iptables 插入一条:-A ...

  8. 详解JQuery Ajax 在asp.net中使用总结

    自从有了JQuery,Ajax的使用变的越来越方便了,但是使用中还是会或多或少的出现一些让人短时间内痛苦的问题.本文暂时总结一些在使用JQuery Ajax中应该注意的问题,如有不恰当或者不完善的地方 ...

  9. Winform的学习

    昨天由于一些原因没有上课啦,虽然也看啦一些东西,但是感觉太少也就没有来啦,嘿嘿,今天认真地了解啦winform,学习了一些控件的使用,但是感觉好多属性知道怎么用的,却还是记得不太清楚,感觉看到啦知道, ...

  10. 【PHP7.1】linux centos7 安装phpredis扩展

    背景: linux  centos7.4  php7.1 一 . 安装redis 1 进入usr/local  目录 cd /usr/local 2  下载redis  并解压到当前目录 wget h ...