标题:[MySQL初阶]MySQL(8)索引机制
@水墨不写bug
文章目录
一、索引的定位
如何提高算法的效率?
具体来说有两种方法:
1.改善数据组织的方式(数据结构)。
2.优化算法本身。
如何理解?以查找一个数据为例:
数据组织在顺序表
中,一般用直接遍历的方法查找数据,复杂度O(N)
。数据组织在AVL树
中,查找一个数据,复杂度O(logN)
。这体现了数据结构不同导致的查找效率的不同。显然AVL树更适合查找这样的场景。
对于算法而言,对效率提升本身有一定影响,比如:可以先快排数组,然后再二分查找,查找效率虽然高了,但是一旦插入一个数据,顺序就乱了。这需要结合具体场景分析
。
MySQL的服务器,本质是运行在内存中的,所有的数据库的CURD操作,全都是在内存
中进行的,索引
也是如此!
其实索引的本质,就是内存中以特定数据组织的方式(数据结构)。
二、观察索引的引入带来的效率提升
首先,我们先通过如下的脚本语言生成百万级别的数据:
SET GLOBAL log_bin_trust_function_creators = 1;
drop database if exists `ddsm_index`;
create database if not exists `ddsm_index` default character set utf8;
use `ddsm_index`;
-- 构建一个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);
操作方法如下:
- 把上面的文件传输到Linux机器上(后缀名.sql,记为createdata.sql),登录自己的MySQL服务,运行:
source createdata.sql的路径+文件名称;
- 在Linux中打开top,查看mysql的CPU占用,一般而言,CUP这时候已经被拉满了。
- 静等创建表完毕。
不加索引直接使用,效率低下
表被创建的目的就是被使用,而如果接下来你直接使用这张表,效率会让你目瞪口呆!
查找empno=998877的员工信息:
select * from EMP where empno=998877;
发现结果:
查询时间达到了低效到惊人的5秒!
加上索引,效率显著提高
当我们加上索引
:
alter table EMP add index(empno);
当然,构建索引本质也是构建一个数据结构,需要一些时间。
构建索引完成后,当我们再查找相同的员工:
select * from EMP where empno=998877;
发现查询时间几乎可以忽略不计:
这就是索引的提高效率的直观体现!
三、索引的硬件基础
1.磁盘
在Linux阶段,我们已经了解过磁盘如何组织文件:
根据磁盘的结构,一个磁盘就是一个小盒子,一下是一个磁盘开盖后的结构。一个磁盘内部有多个盘面,每个盘面都对应有一个磁头。同时,每个盘面上有多个磁道,一个磁道又被分为多个扇区。
如图,磁道是一圈的结构,扇区是磁道的一部分,一个扇区的存储大小是512字节。
2.磁盘的CHS定址法与OS的LBA定址法
如果你对磁盘的CHS定址法不理解,可以查看我的这篇文章:《[Linux] 层层深入理解文件系统——(3)磁盘组织存储的文件》
磁盘是硬件,是通过CHS
进行定址的,每一个地址就是确定了一个扇区(512字节)
。但是其实OS并不是按照CHS定址法来管理磁盘这个硬件的。因为磁盘是硬件,访问硬件是非常低效的!
OS频繁的访问磁盘这个硬件资源会导致整体效率下降。
于是OS为了减少对磁盘的访问,于是加大了一次读取数据的总量:4Kb。没错,这就是一个块!OS对磁盘的读写单位是以块进行的,那么OS对文件系统的管理的地址方法就是逻辑块地址(LBA)
。
本质上,磁盘的CHS定址法对应的是物理地址;而OS的LBA定址法对应的是逻辑地址。
mysql文件本质属于Linux目录树,存储在 /var/lib/mysql 这个目录下,当我们查看这个目录,会发现我们创建的数据库都在这个目录下面。
数据库文件,本质其实就是保存在磁盘的盘片当中。也就是上面的一个个小格子中,就是我们经常所说的扇区。当然,数据库文件很大,也很多,一定需要占据多个扇区。对应OS层面就要占据多个块。
3.磁盘随机访问和连续访问
随机访问
:本次IO所给出的扇区地址和上次IO给出扇区地址不连续,这样的话磁头在两次IO操作之间需要作比较大的移动动作才能重新开始读/写数据。
连续访问
:如果当次IO给出的扇区地址与上次IO结束的扇区地址是连续的,那磁头就能很快的开始这次IO操作,这样的多个IO操作称为连续访问。
因此尽管相邻的两次IO操作在同一时刻发出,但如果它们的请求的扇区地址相差很大的话也只能称为随机访问,而非连续访问。
磁盘是通过机械运动进行寻址的,连续访问不需要过多的定位,故效率比较高。
4.MySQL与磁盘的基本交互单位
mysql是一种特殊的文件系统,有着更高的IO需求场景。所以为了提高基本的IO效率,MySQL进行IO的基本单位是16KB。
5.总结与建立共识
从上面的论述,我们可以得出下面几个共识:
- MySQL以
16KB
为基本单位进行MySQL级别的IO(本质其实是MySQL借助OS来对磁盘进行访问); - MySQL有自己的
buffer poll
:MySQL在启动的时候,会给自己申请一个内存池,在读数据从buffer poll中读取;写数据则写到buffer poll中,进而刷新到文件缓冲区,最后刷新到磁盘。 - 进行系统级IO的时候,要尽可能
减少系统和磁盘IO的次数
。
未完待续~
转载请注明出处