索引:提高数据库的性能,提高数据库的检索速度,不用加内存,不用改sql,不用改程序,速度可以提高成败上千倍,但是插入,更新,删除的速度就比较慢
常见的索引
MySQL的工作过程
我们插入了大量的数据,然后从里面进行查找数据,这样我们就会发现了查询的结果会查很久
我们发现很慢,时间过了很久才能查询出来
alter table EMP add index(empno);
我们在添加索引之后,发现查找的非常块,几乎每花什么时间
select * from EMP where empno=998877;
数据存储效率很低,路上还要有带宽限制,因为我们在传输数据的时候,不仅要在自己的磁盘中传送,还要在网络中进行跨网络传输
所有的数据都是基于LInux文件系统的
要找到一个文件的全部,本质上,就是在磁盘中找到所有保存文件的扇区
磁盘里面:磁头确认的是哪一个面,磁盘确认的是哪个圈,扇区来进行确认是哪一块区域,来定位到某一个扇区(CHS定位方案),磁盘使用的方案
磁盘的使用,使用的LBA,逻辑块地址(可以理解为虚拟地址和物理地址),这个是操作系统使用的方案
结论
我们已经能够在硬件层面上定位到对应的地址了,任何一个数据块(扇区),在系统软件上,直接按照扇区(512字节,会造成过度寻址,所以我们基本都是使用4096来进行寻址,4页)
如果OS直接用硬件提供的数据大小进行交互,那么系统软件,
磁盘的交互都是使用4KB进行交互
连续磁盘访问
MySQL作为一款软件,可以想象成一个特殊的文件系统,它有更高的IO 场景,为了提高效率,
也就是说,磁盘这个硬件的基本单位是512字节,而MySQL使用16kb进行IO交互,即
MySQL和磁盘进行数据交互(通过OS)的基本单位是16KB,这个基本数据单位在mysql里面叫做page
如果它的基本单位和磁盘一样的话,万一磁盘的基本单位变了,它也要跟着变,兼容性不行
所以这里规定MySQL数据交互的基本单位是16KB=1page
IO请求----系统中一定存在大量的IO 请求————操作系统一定也要管理这些IO请求
先描述再组织
struct request_io
{
char * start,*end;
pid_t id;
}
磁盘中,磁盘中一定是要对这些io请求进行管理的
struct disk
{
struct requestt_io* queue;//访问对应的磁盘,都有对应的对应进行保存起来
};
创建表
create table user(
-> id int primary key,--再id这个地方添加主键,建立了一个主键索引
-> age int not null,
-> name varchar(16) not null
-> );
我们这里是故意乱序的进行数据的插入
我们select之后,发现:查询的结果是按照主键顺序进行排序的
我们中断一下,排序的工作是由谁操作的,为什么要这样干呢?
为何IO交互要使用page?
每次交互都使用page(16 KB),这样一次可以预加载很多,可以充分利用局部性原理。1
为何MySQL和磁盘进行IO交互的时候,要采用page的方案进行交互,用多少,加载多少的方式不香吗?
如上面的5条数据,MySQL要查id=2,第一次加载,id=1,第二次加载id=2,一次一条数据,就要进行2次IO,如果要找id=5,就需要5次IO
但是如果5条数据都被保存再page里面(16kb能保存很多记录),第一次IO查找的时候,整个page就直接加载MySQL的buffer pool里面了,这里完成了一次IO,但是往后的id=1,3,4,5等,就不需要进行IO了,而是直接再内存里面进行了,大大减小了IO 的次数
你怎么保证,用户下次找的数据,就再这个page里面,我们不能保证,但是会由很大的概率,因为由局部性原理,往往IO 效率低下唉,最主要矛盾不是单次IO的数据量大小,而是IO 的次数
正式理解索引结构,一定会存在page结构体
- mysql会预先开辟一段空间来保留这些page
- MySQL在,任何一个时刻,一定会存在大量的page页,存在MySQL内部
- MySQL本身也要对page进行管理,也是要先描述再组织
struct page { struct page * prev;//指向前一个page struct page * next;//指向后一个page char buff(16kb-其他字段占用的大小); }
对page的管理,就是对链表的管理
之后的操作都是再内存当中实现的
mysql插入数据时按照主键来进行排序,插入后,page里面就是有序的,查找的时候,就能优化查找算法,有序的就可以提高查找效率,如二分查找,方便我们进行查找
理解多个page
页内目录
用目录查找,效率就很高,第一章是什么,在第几页,这样查找就很快,以空间换时间
先在一个page里面提高搜索效率,那么在所有page里面,效率也提高了很多
有了目录,就能一次淘汰很多数据,这样就会导致单页中数据减少,我们是先遍历数据目录,再去便利数据记录,这样提高的效率就很大,我们先遍历目录,
假如说数据500条,我们目录10条,这样就是只要遍历10次目录,遍历50次目录中对应的数据,大大减少了遍历的次数
page之间也是线性的,怎么提高page之间的搜索效率
page内部的效率问题
page和page之间的效率问题!
数据不断的插入新增,mysql很容易容量不足,所以就需要开辟一个新的page来保存数据,然后通过指针的方式,来将所有的page组织起来
问题
因为我们要保证一个整体有序,所以新来的数据,不会放在新的page里面,
这样我们就可以通过多个page的遍历,page内部用目录进行定位数据,可是这样也有效率问题,在page里面我们也需要用mysql进行遍历,这样就显得我们之前的page,有点没用了,我们查找一个数据,需要把前面的所有page遍历一遍,
解决方案
给我们的一个一个page也带上目录,
把每个page里面最小的记录,作为每个page的键值,这样page和page之间键值就不会重复了
在page里面中在开一个page,里面就只保存下面所管理的主键和指针,只保存一个一个目录
用一个page专门来保存page目录,和page对应的地址,一个page可以管理2000多个page
直接到顶层的page目录
数据量很大的话,还要继续在中间增加page目录
相当于一个b+树,一个节点可以索引到很多地址,至此, 我们就已经可以给我们的表user构建完主键索引了
随便找一个id=?,我们发现,现在查找的page树一定减少了,说明我们IO次数也减少了,那么效率也高了
在page之间,和page内部都添加目录结构,这样就能构建一个b+树
在MySQL内部,将热点数据,以B+树的形式将所有的page页进行组织,形成的数据结构与其配套的查找算法,叫做索引
添加索引,就给业内加目录,给顶层添加目录结构,方便对数据进行筛查
如果没有添加索引,所有的数据都是以链表的形式串联起来,数据没有做优化,没有排序,插入什么样就是什么样的,
有了b+树,每次从叶子节点中,进行搜索,一路找下去,就可以查找到对应的数据了
现在我们就不用加载很多数据到内存中,不需要查找的page就不需要加载到内存中了,我们需要哪个page加载哪个page即可,一路把我们查询过程中的路径page加载进去即可
只要我们把页目录加载进去之后,这些目录就常驻了,以后就不需要再加载到内存了,减少了IO的次数
但是其实目录页本来就没多少,所以可以直接都加载进来,
但是如果插入数据,删除数据的时候,会影响一下效率,因为会影响整个页目录的结构,
修改了一个数据,都要以整个page进行刷新到磁盘中
B+树
创建了一个索引,它就会创建一个文件叫.ibd,index_block_data,这个说明数据和索引合在一起的
把索引和数据都加载进去了
B+树在哪里
- 在磁盘上,有完整的B+和数据
- 在内存中有局部高频被访问的B+核心Page
- mysql查找一定会伴生着mysql进行根据B+进行page的换入换出!
同样,文件也是,内存中,一定是被打开的文件
:数据和索引分离
myisam的叶子节点不存储数据,只存储数据对应的地址,这样就能保存更多键值和索引关系,把数据放到对应的一个地方,用指针进行索引
把索引page和数据page进行分离,也就是叶子节点没有数据,只有对应数据的地址
innodb就是聚簇索引
把数据和索引聚合在一起
辅助索引
MySQL除了会建立主键索引之外,还会建立按照其他列信息建立索引,一般这叫做辅助索引,其他字段也能建立索引
和主键索引没有差别,无非是主键不能重复,而非主键可以重复
主键就是叶子有地址,有索引
同样是构建b+树,innodb非主键索引中叶子节点保存对应地址的索引,但是没有保存数据,太浪费空间了,包含的是对应主键,再根据主键进行查询索引,回调索引,两边索引,先获得主键,再主键索引,获得索引的记录,
附上数据,太浪费空间了,会造成数据的冗余,但是myisam不需要进行回表,查询效率还是回更高一点,但是innodb支持事务,而mysiam不保证事务
下一次访问的数据,有很大概率会和上一次访问的数据重叠 ↩︎