MySQL——索引实现原理
在MySQL中,索引属于存储引擎级别的概念,不同存储引擎对索引的实现方式是不同的,本文主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式。
MyISAM索引实现
MyISAM引擎使用B+Tree作为索引结构。
MyISAM会按照数据插入的顺序分配行号,从0开始,然后按照数据插入的顺序存储在磁盘上。因为行是定长的,所以可以从表的开头跳过相应的字节找到需要的行。

MyISAM的一级索引(主键索引),一个节点包含多个内部节点,索引中的每个叶子节点包含“行号”。假设我们以col1为主键,则下图是一个MyISAM表的主索引(Primary key)示意。

可以看出MyISAM的索引文件仅仅保存数据记录的行号,然后通过此行号回表查询需要的数据。
那col2列上的索引(辅助索引)又会怎么样呢?有什么特别之处吗?答案是否定的,和一级索引(主键索引)没有什么区别。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。如果我们在col2上建立一个辅助索引,则此索引的结构如下图所示:

因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。MyISAM的索引方式索引和数据存放是分开的,非聚集”的,所以也叫做非聚集索引。
InnoDB索引实现
虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。因为InnoDB支持聚簇索引(主键索引),聚簇索引就是表,所以InnoDB不用像MyISAM那样需要独立的行存储。也就是说,InnoDB的数据文件本身就是索引文件。
聚簇索引的每一个叶子节点都包含了主键值、事务ID、用于事务和MVCC的回滚指针以及所有的剩余列。假设我们以col1为主键,则下图是一个InnoDB表的聚簇索引(主键索引)(Primary key)示意。

与MyISAM不同的是,InnoDB的二级索引和聚簇索引很不相同。InnoDB的二级索引的叶子节点存储的不是行号(行指针),而是主键列。这种策略的缺点是二级索引需要两次索引查找,第一次在二级索引中查找主键,第二次在聚簇索引中通过主键查找需要的数据行。
画外音:可以通过我们前面提到过的索引覆盖来避免回表查询,这样就只需要一次回表查询,对于InnoDB而言,就是只需要一次索引查找就可以查询到需要的数据记录,因为需要的数据记录已经被索引到二级索引中,直接就可以找到。
好处是InnoDB在移动行时无需更新一级索引中的这个”指针“,因为主键是不会改变的,但是行指针却会改变。
InnoDB的二级索引示意如图:

使用InnoDB主键应该知道的事项
因为InnoDB的索引的方式通过主键聚集数据,严重依赖主键。索引如果没有定义主键,那么InnoDB会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键来作为聚簇索引。
聚簇索引的优点有:
1.可以把相关数据存储在一起,减少数据查询时的磁盘I/O
2.数据访问更快,因为聚簇索引就是表,索引和数据保存在一个B+Tree中
3.使用索引覆盖的查询时可以直接使用页节点中的主键值
聚簇索引的缺点有:
1.插入速度严重依赖插入顺序
2.更新聚簇索引列的代价很高,因为会强制InnoDB把更新的列移动到新的位置
3.基于聚簇索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能会导致“页分裂”。当行的主键值要求必须将这一行插入到已满的页中时,存储引擎会将该页分裂为两个页面来容纳该行,这就是一次页分裂操作,页分裂会导致表占用更多的存储空间。
画外音:关于页,我们在上一篇文章中也提到过。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页。存和磁盘以页为单位交换数据。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次磁盘I/O就可以完全载入。
基于聚簇索引以上的这些特点,在InnoDB中,我们应该尽量使用和应用无关的主键,例如自增主键,这样可以保证数据行是按照顺序写入的。而不是使用GUID、UUID生成随机的主键。
向聚簇索引中插入顺序的索引值:
每条新纪录总是在前一条记录的后面插入:

当页被插满后,继续插入到新的页:

向聚簇索引中插入随机的索引值:
新的记录可能被插入到之前记录的中间,导致需要强制移动之前的记录:

被写满且已经刷到磁盘上的页可能会被重新读取用于再次插入,此时还需要进行页分裂:

总结
MyISAM和InnoDB两个存储引擎的索引虽然都是使用的B+Tree数据结构,但是在具体实现上还是存在不小差别的。InnoDB支持聚簇索引,聚簇索引就是表,所以InnoDB不用像MyISAM那样需要独立的行存储。也就是说,InnoDB的数据文件本身就是索引文件。而MyISAM的数据文件和索引文件是分开存储的。可以通过MyISAM和InnoDB如何存放表的抽象图帮助快速理解。
InnoDB(聚簇)表分布:

MyISAM(非聚簇)表分布:

参考
《高性能MySQL》
推荐阅读
MySQL——通过EXPLAIN分析SQL的执行计划
MySQL——索引基础
MySQL——索引优化实战
数据库索引背后的数据结构
是什么影响了数据库索引选型?

MySQL——索引实现原理的更多相关文章
- MYSQL索引结构原理、性能分析与优化
[转]MYSQL索引结构原理.性能分析与优化 第一部分:基础知识 索引 官方介绍索引是帮助MySQL高效获取数据的数据结构.笔者理解索引相当于一本书的目录,通过目录就知道要的资料在哪里, 不用一页一页 ...
- 【转】由浅入深探究mysql索引结构原理、性能分析与优化
摘要: 第一部分:基础知识 第二部分:MYISAM和INNODB索引结构 1.简单介绍B-tree B+ tree树 2.MyisAM索引结构 3.Annode索引结构 4.MyisAM索引与Inno ...
- 重新学习MySQL数据库4:Mysql索引实现原理
重新学习Mysql数据库4:Mysql索引实现原理 MySQL索引类型 (https://www.cnblogs.com/luyucheng/p/6289714.html) 一.简介 MySQL目前主 ...
- MySQL索引的原理,B+树、聚集索引和二级索引
MySQL索引的原理,B+树.聚集索引和二级索引的结构分析 一.索引类型 1.1 B树 1.2 B+树 1.3 哈希索引 1.4 聚集索引(clusterd index) 1.5 二级索引(secon ...
- mysql索引工作原理、分类
一.概述 在mysql中,索引(index)又叫键(key),它是存储引擎用于快速找到所需记录的一种数据结构.在越来越大的表中,索引是对查询性能优化最有效的手段,索引对性能影响非常关键.另外,mysq ...
- 重新学习Mysql数据库4:Mysql索引实现原理和相关数据结构算法
本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...
- MySQL索引结构原理分析
我们在学习MySQL的时候经常会听到索引这个词,大概也知道这是什么,但是深究下去又说不出什么道道来.下面将会比较全面的介绍一下关于索引! 1 索引是什么? 这里用百度百科的一句话来说,在关系数据库中, ...
- Mysql 索引实现原理. 聚集索引, 非聚集索引
Mysql索引实现: B-tree,B是balance,一般用于数据库的索引.使用B-tree结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度.而B+tree是B-tree的一个变种,My ...
- mysql 索引及其原理
mysql 索引 KEY与INDEX的区别: KEY is something on the logical level, describes your table and database desi ...
随机推荐
- mac 查询端口被哪个进程占用
1,查看端口被哪个程序占用sudo lsof -i tcp:port如: sudo lsof -i tcp:80802,看到进程的PID,可以将进程杀死.sudo kill -9 PID如:sudo ...
- 在Android中调用KSOAP2库访问webservice服务出现的服务端返回AnyType{}
最近在做毕业设计的时候,涉及到了安卓端访问web service服务端数据库,并返回一个值,当我把web service测试通过后,想写一个简单的安卓测试程序,来实现服务端数据库访问,通过web se ...
- Android开发艺术探索学习笔记(十)
第十章 Android的消息机制 面试中经常会被问到的一个问题:handler是如何在子线程和主线程中进行消息的传递的,这个问题通过了解Android的消息机制可以得到一个准确的答案. Androi ...
- 一:理解ASP.NET的运行机制(例:通过HttpModule来计算页面执行时间)
一:简要介绍一下asp.net的执行步骤 1.IIS接收到客户请求 2. IIS把请求交给aspnet_isapi.dll处理 3.(如果是第一次运行程序)装载bin目录中的dll 4.(如果是第一次 ...
- Vue中使用watch来监听数据变化
写法一: methods:{ //监听isMD upProp(){ if(this.isMD){//如果isMD等于true 就把storeManagerName赋值给isStoreManagerNa ...
- 插入排序:直接插入排序&希尔排序
一.直接插入排序 1. 思想 直接排序法, 可以分为两个部分, 一部分是有序的, 一部分是无序的. 从这个图上, 应该是能看清楚直接插入排序的思想了. 将无序部分的第一个与有序部分进行比较. 从有序部 ...
- shell脚本案例分享 - 业务系统日志自定义保留或删除需求
需求说明: 线上某些业务系统的日志不定期产生, 有的每天产生, 有的好几天才产生, 因为系统只有在用的时候才产生日志,日志文件均存放在以当天日期命名的目录下. 当日志目录越来越多时就需要处理, 由此 ...
- SSL编程(2).NET最简单的客户端
在Windows平台上,实现SSL的.NET类是System.Net.Security.SslStream类.这个类既可以用于建立SSL服务,也可以用来作为SSL客户端连接远端的SSL服务. 最简 ...
- Spring事务内方法调用自身事务 增强的三种方式
ServiceA.java文件: 查看Spring Tx的相关日志: 可以看到只创建了一个事物ServiceA.service方法的事务,但是callSelf方法却没有被事务增强; 分析原因:Spr ...
- 34.Linux-printk分析、使用__FILE__, __FUNCTION__, __LINE__ 调试
本节学习目的 1)分析printk()函数 2)使用printk()调试驱动 1.在驱动调试中,使用printk(),是最简单,最方便的办法 当uboot的命令行里的“console=tty1”时,表 ...