1、什么是索引?
索引:提高数据库的性能,索引是物美价廉的东西了。不用加内存,不用改程序,不用调sql,只要执行正确的create index ,查询速度就可能提高成百上千倍。但是天下没有免费的午餐,查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的IO。所以它的价值,在于提高一个海量数据的检索速度。
常见索引分为:
主键索引(primary key)
唯一索引(unique)
普通索引(index)
全文索引(fulltext)–解决中子文索引问题。
w
2、认识磁盘
cpu先是找到存储位置对应的盘面然后找找到对应的柱面接着找到对应的扇区,这就是CHS寻址方式寻址
那没事什么盘面,柱面,磁道,扇区?
扇区
数据库文件,本质其实就是保存在磁盘的盘片当中。也就是上面的一个个小格子中,就是我们经常所说的扇区。当然,数据库文件很大,也很多,一定需要占据多个扇区。
所以,最基本的,找到一个文件的全部,本质,就是在磁盘找到所有保存文件的扇区。
而我们能够定位任何一个扇区,那么便能找到所有扇区,因为查找方式是一样的。
柱面(磁道):
多盘磁盘,每盘都是双面,大小完全相等。那么同半径的磁道,整体上便构成了一个柱面
定位扇区
柱面多盘磁盘,每盘都是双面,大小完全相等。那么同半径的磁道,整体上便构成了一个柱面
每个盘面都有一个磁头,那么磁头和盘面的对应关系便是1对1的
所以,我们只需要知道,磁头(Heads)、柱面(Cylinder)(等价于磁道)、扇区(Sector)对应的编号。即可在磁盘上定位所要访问的扇区。这种磁盘数据定位方式叫做CHS 。不过实际系统软件使用的并不是CHS (但是硬件是),而是LBA ,一种线性地址,可以想象成虚拟地址与物理地址。系统将LBA 地址最后会转化成为CHS ,交给磁盘去进行数据读取。不过,我们现在不关心转化细节,知道这个东西,让我们逻辑自洽起来即可。
磁盘随机访问与连续访问
随机访问:本次IO所给出的扇区地址和上次IO给出扇区地址不连续,这样的话磁头在两次IO操作之间需要作比较大的移动动作才能重新开始读/写数据。
3、mysql和硬盘交互的基本单位
MySQL也是一个特殊的文件系统,他有着更高的IO场景,所以为了提高IO效率,mysql进行了IO的基本单位时16kb(后边统一使用innodb存储引擎讲解)
也即是说,磁盘这个硬件的基本存储单位就是字节,二mysql存储引擎使用的是16kb进行IO交互,即使mysql和磁盘进行数据交互的基本单位就是16kb这个是基本数据单元在MySQL中叫做page
4、建立共识
MySQL 中的数据文件,是以page为单位保存在磁盘当中的。
MySQL 的CURD 操作,都需要通过计算,找到对应的插入位置,或者找到对应要修改或者查询的数据。
而只要涉及计算,就需要CPU参与,而为了便于CPU参与,一定要能够先将数据移动到内存当中。
所以在特定时间内,数据一定是磁盘中有,内存中也有。后续操作完内存数据之后,以特定的刷新策略,刷新
到磁盘。而这时,就涉及到磁盘和内存的数据交互,也就是IO了。而此时IO的基本单位就是Page。
为了更好的进行上面的操作, MySQL 服务器在内存中运行的时候,在服务器内部,就申请了被称为Buffer
Pool 的的大内存空间,来进行各种缓存。其实就是很大的内存空间,来和磁盘数据进行IO交互。
为何更高的效率,一定要尽可能的减少系统和磁盘IO的次数
5、索引的理解
首先建立一张测试表
CREATE TABLE `user` ( `id` int(11) NOT NULL, `age` int(11) NOT NULL, `name` varchar(16) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
其中id为主键不为空
在插入数据时id是按照无须的插入的
但是为什么显示id为什么是有序的呢?谁干得呢?
IO为什么会要page
为何mysql和磁盘进行交互的时要采用page进行交互呢?为什么不要用多少,就加载多少呢?
试想一想假如mysql想要上边的数据每次只拿一条数据,但是每一次拿数据就要一次IO,大家都知道每一次IO就是一次的寻址拿数据浪费的时间很多,所以为了防止多次IO所以就将多条数据保存在page中。
假如第一次拿id为2数据所以就顺便就把id为1345的数据就拿上来了然后加载到mysql中的buff pool中这一次IO拿到5条数据,后边读取id为1345的数据时就不需要进行IO了,所以但Page就大大减少了IO的次数。
但是也有可能有的数据也没在page中但是还是有很大可能是有的,所以和着相比大可不必。
理解单个page
mysql中要管理很多数据表的文件,而要管理这些文件就需要先描述在组织,我们可以简单理解为独立文件是一个page或者多个page构成的.
可以将单个page想象为一个双向链表的一个节点,节点之间可以相互存折下一个或者前一个的节点地地址。来一个page就将他连接到链表的尾部。
不同的Page ,在MySQL 中,都是16KB ,使用prev 和next 构成双向链表
因为有主键的问题, MySQL 会默认按照主键给我们的数据进行排序,从上面的Page内数据记录可以看出,数据是有序且彼此关联的。
为什么数据库在插入数据时要对其进行排序呢?我们按正常顺序插入数据不是也挺好的吗?
插入数据时排序的目的,就是优化查询的效率。页内部存放数据的模块,实质上也是一个链表的结构,链表的特点也就是增删快,查询修改慢,所以优化查询的效率是必须的。
正式因为有序,在查找的时候,从头到后都是有效查找,没有任何一个查找是浪费的,而且,如果运气好,是可以提前结束查找过程的。
理解多个page页面
通过上面的分析,我们知道,上面页模式中,只有一个功能,就是在查询某条数据的时候直接将一整页的数据加载到内存中,以减少硬盘IO次数,从而提高性能。但是,我们也可以看到,现在的页模式内部,实际上是采用了链表的结构,前一条数据指向后一条数据,本质上还是通过数据的逐条比较来取出特定的数据。
如果有1千万条数据,一定需要多个Page来保存1千万条数据,多个Page彼此使用双链表链接起来,而且每个Page内部的数据也是基于链表的。那么,查找特定一条记录,也一定是线性查找。这效率也太低了。
引入页目录
单页情况:
针对上面的单页Page,我们能否也引入目录呢?当然可以
那么当前,在一个Page内部,我们引入了目录。比如,我们要查找id=4记录,之前必须线性遍历4次,才能拿到结果。现在直接通过目录2[3],直接进行定位新的起始位置,提高了效率。现在我们可以再次正式回答上面的问题了,
为何通过键值MySQL 会自动排序?
可以很方便引入目录
多页情况
在MySQL中么可以页地大小是固定的但是随着访问数据量的增加蛋哥page也就不够了,所以就会有多个page来此存储
Page内部通过目录来快速定位数数据,但是page之间还是使用指针来相互连接,在mysql容量空间不够时,会自动开辟新的page来存储新的数据然后将新的page连接到page链表的后边,将所有page组织起来。
目前要保证整体有序,那么新插入的数据,不一定会在新Page上面
但是这样还是会在多个page之间线性检测,还需要mysql在多个page之间遍历,遍历意味着达大量的IO,将下一个page加载到内存,进行线性检测,这还是浪费了时间,效率还是低效的。
有没有别的方法解决呢?解决法案就是之前地方案就是加目录,给page也带上目录
使用一个目录项来指向某一页,而这个目录项存放的就是将要指向的页中存放的最小数据的键值。
和页内目录不同的地方在于,这种目录管理的级别是页,而页内目录管理的级别是行。
其中,每个目录项的构成是:键值+指针和page中的目录相似。图中没有画全。
存在一个目录页来管理页目录,目录页中的数据存放的就是指向的那一页中最小的数据。有数据,就可通过比较,找到该访问那个Page,进而通过指针,找到下一个Page。
目录的本质就是page,普通page存储的是数据但是目录中存储的是普通page地地址,但是随着目录地增加,放问目录之间也是线性地,也需要mysq遍历,遍历意味着IO也需要浪费大量的时间,效率也不是特别高。我们也需要给目录加上目录,形成多级目录。在加载目录不需要将全部数据加到内存空间中
当需要拿数据时先从一级目录开始查找,找到属于这个数据范围的目录然后加载下一级该范围目录,不需要的就不需要记载加载到内存空间中,只需要拿一部分,接着拿下一级目录,直到拿到相应地page,直到拿到需要的数据。我们发现,现在查找的Page数一定减少了,也就意味着IO次数减少了,那么效率也就提高了。
这样的多级目录组成的page集合就是B+树,在内存中page就是以B+树形式存在的。除了叶子节点其他节点不携带任何有效数据
Page分为目录页和数据页。目录页只放各个下级Page的最小键值。
好处:目录page的大小是固定的,意味着保存的数据量而是固定,不存数据,就能存储更多的索引信息,目录page就能管理更多的page
否则目录page管理地数据也太少,整个树的层数太高,意味着从一级目录找到目的page经历的目录page也就愈多,每加载一个page就需要一次IO,这样就需要更多的IO次数。所以这颗树更低矮。不需要更多次的IO了
查找的时候,自定向下找,只需要加载部分目录页到内存,即可完成算法的整个查找过程,大大减少了IO次数
InnoDB 在建立索引结构来管理数据的时候,其他数据结构为何不行?
链表?线性遍历
二叉搜索树?退化问题,可能退化成为线性结构
AVL &&红黑树?虽然是平衡或者近似平衡,但是毕竟是二叉结构,相比较多阶B+,意味着树整体过高,大家都
是自顶向下找,层高越低,意味着系统与硬盘更少的IO Page交互。虽然你很秀,但是有更秀的。
Hash?官方的索引实现方式中, MySQL 是支持HASH的,不过InnoDB 和MyISAM 并不支持.Hash跟进其算法特
征,决定了虽然有时候也很快(O(1)),不过,在面对范围查找就明显不行,另外还有其他差别,有兴趣可以查一
下。
B树?最值得比较的是InnoDB 为何不用B树作为底层索引?
先看一下B树得结构
因为在目录page中存储了有效数据,但是这样可管理的page就会更少,这样B树得层数就会更高,从一级目录到目的page就会经过更多得目录page,就会经过更多的IO,浪费更多的时间。再看一下B+树结构?
可以看到B+树得目录不存储任何有效数据,这样就会管理更多的目录page这样B+树得层数就会更低矮,访问的目录就会越少。节点不存储data,这样一个节点就可以存储更多的key。可以使得树更矮,所以IO操作次数更少。叶子节点相连,更便于进行范围查找
聚簇索引 VS 非聚簇索引
MyISAM 引擎同样使用B+树作为索引结果,叶节点的data域存放的是数据记录的地址。下图为MyISAM 表的主索引, Col1 为主键。
MyISAM 将page和数据分离,也就是说叶子节点存储得不是不是数据而是对于数据的地址
相较于InnoDB 索引, InnoDB 是将索引和数据放在一起的。
创建一个基于非聚簇索引得数据库表
在创建表时就会自动将索引创建好
可以看出数据文件和索引文件时分离得MyISAM 这种用户数据与索引数据分离的索引方案,叫做非聚簇索引
创建一个基于InnoDB 的数据库文件
可以看到数据和索引数据在一个文件中。
其中, InnoDB 这种用户数据与索引数据在一起索引方案,叫做聚簇索引
当然, MySQL 除了默认会建立主键索引外,我们用户也有可能建立按照其他列信息建立的索引,一般这种索引可以
叫做辅助(普通)索引。
MyISAM 非主键索引图
MyISAM非主键索引和主键索引没什么大的区别也就是根据目录page找到目的page,目的page存储的是相应的数据的地址,基本没有什么区别。
同样, InnoDB 除了主键索引,用户也会建立辅助(普通)索引,我们以上表中的Col3 建立对应的辅助索引如下图:
先找到InnoDB 的辅助索引找到对应的键值,再根据辅助索引对应的键值找到搜索对应的主键B+树对应的记录值,当innoDB找的是辅助索引就需要二次回调
InnoDB和MyISAM 的区别?
MyISAM 在删除数据时首先就需要改变接着就需要该索引,想要增加数据时也要需要该改变表结构还有索引。虽然MyISAM 在删除数据和增加数据的效率比较低,但是在查询效率上不比InnoDB低
InnoDB在删除数据和增加数据时不需要修该表结构和索引,只需要直接在B+中直接删除和增加就行。