索引
索引是什么
在课件上对于索引的定义是这样子的
索引:提高数据库的性能,索引是物美价廉的东西了。不用加内存,不用改程序,不用调sql,只要执行正确的 create index ,查询速度就可能提高成百上千倍。但是天下没有免费的午餐,查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的IO。所以它的价值,在于提高一个海量数据的检索速度。
总结下上面这段话
索引是mysql的一个特性 使用它可以增加mysql的查找效率 但是可能会牺牲一部分IO效率
索引的分类
常见的索引分类如下
- 主键索引(primary key)
- 唯一索引(unique)
- 普通索引(index)
- 全文索引(fulltext)–解决中子文索引问题
关于这个分类 大家现在不理解也没关系 等看完整篇博客之后就能对于它们有一个清晰的认知
索引作用查看
我们这里创建一张海量数据的数据库表(八万条数据) 演示在有和没有索引的情况下 效率的差别
我们可以使用下面的sql来创建一个海量数据表 (直接复制粘贴到mysql终端即可)
drop database if exists `index_demon`; create database if not exists `index_demon` default character set utf8; use `index_demon`; -- 构建一个8000000条记录的数据 -- 构建的海量表数据需要有差异性,所以使用存储过程来创建 -- 产生随机字符串 delimiter $$ create function rand_string(n INT) returns varchar(255) begin declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'; declare return_str varchar(255) default ''; declare i int default 0; while i < n do set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1)); set i = i + 1; end while; return return_str; end $$ delimiter ; -- 产生随机数字 delimiter $$ create function rand_num( ) returns int(5) begin declare i int default 0; set i = floor(10+rand()*500); return i; end $$ delimiter ; -- 创建存储过程,向雇员表添加海量数据 delimiter $$ create procedure insert_emp(in start int(10),in max_num int(10)) begin declare i int default 0; set autocommit = 0; repeat set i = i + 1; insert into EMP values ((start+i) ,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num()); until i = max_num end repeat; commit; end $$ delimiter ; -- 雇员表 CREATE TABLE `EMP` ( `empno` int(6) unsigned zerofill NOT NULL COMMENT '雇员编号', `ename` varchar(10) DEFAULT NULL COMMENT '雇员姓名', `job` varchar(9) DEFAULT NULL COMMENT '雇员职位', `mgr` int(4) unsigned zerofill DEFAULT NULL COMMENT '雇员领导编号', `hiredate` datetime DEFAULT NULL COMMENT '雇佣时间', `sal` decimal(7,2) DEFAULT NULL COMMENT '工资月薪', `comm` decimal(7,2) DEFAULT NULL COMMENT '奖金', `deptno` int(2) unsigned zerofill DEFAULT NULL COMMENT '部门编号' ); -- 执行存储过程,添加8000000条记录 call insert_emp(100001, 8000000);
上述SQL中创建了一个名为index_demon的数据库 在该数据库中创建了一个名为EMP的员工表 并向表当中插入了八百万条记录
建立该数据库需要大概七分钟左右的时间 大家耐心等待
创建完毕之后我们即可使用index_demo数据库和里面的EMP表 下面开始我们的对比工作
查询员工编号为998877编号的员工
我们可以发现 花费了4.35秒才找到了该条记录
创建索引
对这八百万条记录创建索引我们用了20.33秒
重新查询该员工的数据
重新查询之后进步十分明显 直接从4.35秒降低到了0秒
这里就说明一个道理 索引对于海量数据查询效率的提升是十分显著的
磁盘
mysql的工作过程
宏观过程
由于冯诺伊曼体系我们可以知道 mysql不能对于磁盘中的数据库进行直接访问 它的访问一定是要经过操作系统的内核缓冲区的
大概的运行过程如上图
- mysql在内存中申请一大块内存空间 我们一般叫做buff pool
- mysql对于数据的curd都是在内存中进行操作的
- mysql调用系统函数write之后这些数据会写入操作系统的内核缓冲区当中
- 操作系统的内核缓冲区会定期刷新数据到磁盘中的数据库
认识磁盘
磁盘的整体结果如下
我们再来看看磁盘中的一个盘片
盘片中又被分为磁道和扇区 如上图所示 我们这里对于磁盘的整体结构和扇区概念有一个认知即可
其中扇区的大小大部分都是512字节
而我们前面说过 数据库的文件都保存在磁盘当中 所以说我们查找数据的本质就是在定位扇区
定位扇区
我们在硬件层面定位扇区的方式叫做CHS
- 首先找到磁头来确定文件在哪一个盘面
- 之后在该盘面中找到数据所在的磁道
- 最后在磁道中找到对于的扇区
但是在软件层面上我们使用的定位方式叫做LBA(一种线性地址) 我们可以把它们之间的关系想象成虚拟地址和物理地址之前的关系来方便理解
在软件层面上通过LBA定位到以后最终还是要使用CHS来定位具体的物理空间
操作系统和磁盘交互的基本单位
我们现在已经能够在硬件层面定位扇区了 那么在系统软件上我们就直接按照扇区的基本单位(大部分512字节)进行交互嘛?
答案是否定的 原因有以下几点
- 如果操作系统使用硬件提供的基本单位进行交互 那么操作系统和硬件之间就会产生强相关的关系 一旦硬件发生大的改变 操作系统势必也要发生改变
- 我们都知道 IO的效率是非常非常慢的 单次IO512字节还是太小了 而每次读取很小的数据势必会造成更多次的IO
- 我们在基础IO时学习文件系统 文件系统就是在磁盘的基本结构下建立的 而它的基本单位不是扇区 而是数据块
综上所述 系统读取磁盘是以块为单位的 大小为4kb
磁盘随机访问(Random Access)与连续访问(Sequential Access)
- 随机访问:本次IO所给出的扇区地址和上次IO给出扇区地址不连续,这样的话磁头在两次IO操作之间需要作比较大的移动动作才能重新开始读/写数据。
- 连续访问:如果当次IO给出的扇区地址与上次IO结束的扇区地址是连续的,那磁头就能很快的开始这次IO操作,这样的多个IO操作称为连续访问。
因此尽管相邻的两次IO操作在同一时刻发出,但如果它们的请求的扇区地址相差很大的话也只能称为随机访问,而非连续访问。
磁盘是通过机械运动进行寻址的,随机访问不需要过多的定位,故效率比较高。
mysql和磁盘交互的基本单位
mysql和磁盘交互这句话其实并不准确 因为mysql没有资格直接和磁盘进行交互 它们之间一定是要经过操作系统的 但是我们学习这部分的时候为了方便理解可以暂时忽略操作系统
MySQL 作为一款应用软件,可以想象成一种特殊的文件系统。它有着更高的IO场景,所以为了提高基本的IO效率, MySQL 进行IO的基本单位是 16KB (后面统一使用 InnoDB 存储引擎讲解)
我们可以使用下面的sql语句来查看mysql交互的IO大小 我们可以发现是16384字节 实际也就是16kb
根据上面讲解的知识总结一些共识
- MySQL 中的数据文件 是以page为单位保存在磁盘当中的
- MySQL 的 CURD 操作 都需要通过计算 找到对应的插入位置 或者找到对应要修改或者查询的数据
- 而只要涉及计算 就需要CPU参与 而为了便于CPU参与 一定要能够先将数据移动到内存当中
- 所以在特定时间内 数据一定是磁盘中有 内存中也有 后续操作完内存数据之后 以特定的刷新策略刷新到磁盘 而这时就涉及到磁盘和内存的数据交互 也就是IO了 而此时IO的基本单位就Page
- 为了更好的进行上面的操作 MySQL 服务器在内存中运行的时候 在服务器内部 就申请了被称为 Buffer Pool 的的大内存空间 来进行各种缓存 其实就是很大的内存空间来和磁盘数据进行IO交互
- 为了更高的效率 要尽量减少系统和磁盘IO的次数
如何理解IO请求
首先我们可以明确一点 系统中肯定会存在大量的IO请求 而操作系统作为软硬件资源的管理者肯定要对这些资源进行管理 那么问题又来了 应该如何管理呢? - - 先描述 再组织
我们都知道Linux操作系统是由C语言写的 而C语言中描述一个对象所使用的方式是结构体
所以说IO请求在Linux操作系统中的形式其实就是结构体 这些结构体再被通过双链表的形式组织起来 这就是操作系统对于IO请求的管理