初识mysql数据库之索引概念与磁盘效率问题

目录

一、索引的概念及作用

二、实际看看索引的效率提升

三、认识磁盘

1. 简单了解磁盘

2. 数据库文件存储位置

3. 定位扇区

4. 数据读取效率问题

5. 磁盘随机访问与磁盘连续访问

5.1 随机访问

5.2 连续访问

四、mysql与磁盘的交互

五、建立共识 


一、索引的概念及作用

索引,其实就是用于提高数据库的性能的。使用它不用加内存、不用改程序、不用调sql,只需要执行正确的“create index”,就可以让数据库的查询速度提高成百上千倍。

当然,数据库查询效率的提高也是有代价,那就是在插入、更新、删除的时候会增加大量的IO,拉低效率。因此,索引的价值仅仅体现在提高海量数据的检索速度上

常见的索引一般分为如下几种:
主键索引(primary key)唯一索引(unique)普通索引(index)全文索引(fulltext)

注意,mysql的服务器是在内存中的,因此,当mysql启动后,它会在内存中为我们开辟一块空间,当需要修改数据时,就会将磁盘中的数据加载到这块内存中,在内存中执行CURD操作,然后在合适的时间将内存中的数据刷新到外设中。索引也是如此。

大家知道,在实际中如果我们要提高搜索效率,其实就是对搜索的算法进行优化。对算法优化一般包含两个方面,一个是数据结构本身,例如将线性结构换成二叉树结构。第二种就是修改算法本身,即优化在特定数据结构下的查找方法,例如在线性表中不再线性遍历,而是二分查找。一般而言,数据结构改变,算法本身也需要跟着改变

对于索引而言也是如此。在索引中就是将数据存储到了特定的数据结构中,利用这个数据结构的优势提高搜索效率。因此,所谓的索引,其实就是在一个特定的数据结构中搜素数据

二、实际看看索引的效率提升

为了方便看到索引带来的效果,所以我们需要准备一份非常大的数据进行索引。

初识mysql数据库之索引概念与磁盘效率问题_第1张图片

在这里准备了这样的一张表,然后向里面插入800w行数据。如果大家也想在自己的机器上实际看到索引的效果,可以到网上直接搜索存储大量数据的数据表,有现成的代码,大家直接复制就可以向特定表中插入大量的数据。

注意,当插入成百上千万行数据时,根据你的配置,插入需要的时间可能不同,配置好的话可能几分钟,配置差点可能就要10几分钟乃至更高。大家看到mysql一直下面的插入状态的话,不要去结束它。

初识mysql数据库之索引概念与磁盘效率问题_第2张图片

还有一个点,在你复制的代码中检查一下数据表的创建里面有没有带上主键、唯一键等属性,如果有,将它去除掉,以方便看到一份“没有索引”的表的搜索效率

当数据插入完成后,千万不要直接输入“select * from 数据表”查看数据表内容,因为里面的数据量非常大,如果直接用该命令而不带筛选选项,mysql就会不断显示数据。

有了这份存储有大量数据的表后,我们搜索一份数据:

初识mysql数据库之索引概念与磁盘效率问题_第3张图片

可以看到,在这份存储有800w行数据的表内查询一个数据,花费了5.65s。很明显,这是不能接受的。要知道,一个公司的数据库中有个几百上千万行数据是非常正常的,如果一个用户查询一条数据,数据库要用5s才能返回数据,很明显是用户所不能接受。并且这仅仅是一个用户查,如果是成百上千乃至更多用户同时查数据,那么数据库的响应速度会更慢,甚至直接挂掉。

由此,我们就必须要想办法提高数据库中数据的查询效率。

输入“alter table 数据库名 add index(列名)”,指定一列为其添加索引。

索引添加完后,我们再到数据库中查询同一份数据:

初识mysql数据库之索引概念与磁盘效率问题_第4张图片

可以看到,当我们为表添加索引后再去表中搜索数据,它的搜索时间就变为了0s。几乎就是在一瞬间就搜索完成了。通过这个例子,就可以明显的看到索引带来的查询效率提升。

三、认识磁盘

1. 简单了解磁盘

我们知道,一般来讲,数据都是存储在磁盘中的。因此,mysql作为一个给用户提供数据存储服务的服务端,它也是将用户数据存储到磁盘这个外设中的。但我们知道,磁盘作为计算机中的一个机械设备,相比与计算机中的其他电子元件,磁盘的存储效率是比较低的。再加上数据IO本身还有一定的效率消耗,如何提升mysql的效率就是一个重要问题了。

由于本章的重点并不是研究硬件,并且在我以前关于linux的文章中也介绍过磁盘的结构了,这里就简要介绍一下。

一块普通的磁盘,它的物理结构如下所示:

初识mysql数据库之索引概念与磁盘效率问题_第5张图片

在这里面的圆盘,就是盘片,上面是有起伏的,每个起伏就代表着二进制,我们的数据就是存储在这块盘片中的。在盘片的上下有一个磁头,它会左右来回摆动,磁头的作用就是读取盘片中保存的二进制数据。在盘片的中间有一个主轴,这个主轴中有一个马达,用于让盘片高速旋转。

注意,磁盘上的盘片是一摞,而不是一片。在这些盘片的上下两面都可以存储数据,这就意味着每一面都有一个磁头在来回摆动读取数据

初识mysql数据库之索引概念与磁盘效率问题_第6张图片

在盘片中,磁盘表面被分为许多个同心圆每个同心圆都称为一个磁道每个磁道都有一个编号最外面的就是0号磁道

每个磁道又被划分为若干段(段又叫扇区)每个扇区的存储容量都是相同的,一般为512字节,每个扇区都有一个编号。但是随着磁盘的发展,现在的扇区正在逐渐扩大,已经出现了更高效的4096字节扇区,被称为“4K扇区”。当然,因为我们不是专门研究磁盘的,不必太过关心,有个大致了解即可。

注意,虽然磁盘上每个扇区的实际大小有差距,但是上面的存储容量都是一样的,因为每个扇区的存储数据量不是看扇区大小,而是看数据存储密度。虽然上文中说磁盘上每个扇区的大小是一样的,但这并不绝对,随着磁盘的发展,已经慢慢出现了扇区容量不一样的磁盘了。在这里暂时不做考虑。

2. 数据库文件存储位置

在数据库中的数据库文件,无论是数据库,还是表,还是表中的数据,其本质都是被保存在磁盘的盘片中的一个个扇区内的。当然,由于一个扇区的存储容量很小,所以当数据库文件很大是,就需要占据多个扇区。

就算是我们当前使用的linux中所看到的大部分目录或文件,其实也是保存在硬盘中的。(注意,有一些内存文件系统,如porc、sys等,我们不做考虑)。

由此,找到一个文件,本质就是在磁盘上找到保存该文件的扇区。这也就要求磁盘要有能够定位任意一个扇区的能力

3. 定位扇区

初识mysql数据库之索引概念与磁盘效率问题_第7张图片

上图是一张有三个磁盘六个盘面的磁盘。在这个磁盘中,每一面都有一个对应的磁头用于读取数据。当我们需要查找某份数据时,就是先确定它在哪一面,找到数据所处的盘面后,再找到它所在磁道, 然后找到数据在该磁道上的哪一个扇区。当找到数据所在扇区后,就可以通过磁头读取数据

由此,我们只需要知道磁头、柱面(等价于磁道)、扇区对应的编号,就可以在磁盘上定位所要访问的扇区,这种磁盘数据定位方式叫做CHS。不过实际系统软件中使用的不是CHS,而是LBA,是一种线性地址,大家可以LBA和CHS分别想象为虚拟地址和物理地址。系统会将LBA地址转化为CHS,交给磁盘去进行数据读取。

4. 数据读取效率问题

通过上面的内容大家应该就知道磁盘是可以定义任意一个扇区的。但是,在系统层面上来看,难道系统软件就是直接按一个扇区(512字节或4096字节)进行IO交互的吗?其实并不是。

这里存在几个原因。

(1)如果OS直接使用硬件提供的数据大小进行交互,这就意味着系统的IO代码和硬件强相关,当硬件发生变化时,系统的IO也就必须跟着变化。

(2)从实际来看,以512字节进行IO还是太小了。IO单位过小,就意味着读取同样的数据内容,需要进行更多的磁盘访问,会带来效率的降低。

(3)大家应该了解过文件系统,文件系统中读取数据的基本单位就不是扇区,而是数据块。一个数据块的大小是4KB。

因此,系统读取磁盘时,并不是以扇区为单位,而是以数据块为单位,基本单位是4KB

至于为什么一个数据块是4KB,一个方面是内存管理的问题。磁盘数据是需要加载到内存中的,而内存中本身就是以4KB划分成多个数据块的,因此,以数据块为基本单位与磁盘交互,更有利于磁盘与内存的IO交互。

另一个理由就是效率问题。在磁盘读取中,就算系统只需要1字节的数据,也需要从磁盘中读取4KB,有人可能认为这是一种浪费,但是这是从效率上考虑过的。因为系统读取数据时,它读取的数据大概率都是存放在一起的,而一次性读取4KB数据,就可以做到数据的预加载,当系统再次读取数据时,它读取的数据大概率就是已经提前加载好的数据,以此减少系统与磁盘的IO次数,提高数据的读取效率。

5. 磁盘随机访问与磁盘连续访问

5.1 随机访问

随机访问,是指本次IO所给出的扇区地址和上次IO给出的扇区地址不连续,这样的话磁头在两次IO操作之间需要作比较大的移动动作才能重新开始读写数据。

5.2 连续访问

如果当次IO给出的扇区地址与上次IO结束的扇区地址是连续的,那磁头就能够很快的开始这次IO操作。这样的多个IO操作称为连续访问。

注意,哪怕相邻的两次IO操作在同一时刻发出,但如果它们的请求的扇区地址相差很大的话,也只能称为随机访问,而非连续访问。

因为连续访问的效率要比随机访问高,所以大家涉及IO的程序中,尽量还是要体现出连续访问,而非随机访问。但是要注意,磁盘的效率问题并不是这么简单就能够解决的,磁盘中也是有很多方案设计用于提高效率的。这里只是简单介绍一下。

四、mysql与磁盘的交互

mysql作为一个应用软件,可以想象为一种特殊的文件系统。它有着更高的IO需求。因此,为了提高基本的IO效率,mysql进行IO的基本单位是16KB

这也就意味着,虽然磁盘的基本单位是512字节,但是MYSQL中的InnoDB引擎使用16KB进行IO交互。即mysql和磁盘进行数据交互的基本单位是16KB。这个基本数据单元,在mysql中叫做“page”(注意,这个page和系统的page不同,要将两者区分开来)。

但是大家知道,在OS中,应用软件是无法直接与外设交互的,必须通过OS。因此,当mysql要与磁盘IO时,并不是双方直接交互,而是OS从磁盘中读取4个数据块,即16KB的数据放到文件系统的文件缓冲区内,而在文件系统中的一个文件缓冲区,其实就可以看成是文件系统打开的一个文件,这个文件会返回一个文件描述符给mysql,让mysql用这个文件描述符读取数据。当然,这并不准确。在mysql中也是存在一个自己的“buffer pool”的,大家可以将其看做一个存储数据的空间。mysql所有的CURD操作都是在这个buffer pool中完成的。

初识mysql数据库之索引概念与磁盘效率问题_第8张图片

至于mysql中写入的数据,就是交给OS,然后OS在满足一定条件后将数据刷新到磁盘上即可。通过这种方式,就提高了mysql的IO效率。

大家也可以在自己的linux中的mysql下输入“show global status like 'innodb_page_size';”命令查看使用InnoDB引擎的mysql下的page大小:

初识mysql数据库之索引概念与磁盘效率问题_第9张图片

可以看到,大小为16384,单位是字节,换算过来就是16KB。

五、建立共识 

 通过上面的内容,我们就需要建立以下几个共识:

(1)mysql中的数据文件,是以page为单位保存在磁盘当中的。

(2)mysql的CURD操作,都需要通过计算找到对应的插入位置,或者找到对应要修或查询的数据

(3)在计算机中只要涉及到计算,就需要CPU参与,而为了方便CPU参与,就需要将数据移动到内存中

(4)因为要将数据移动到内存中,这就意味着在特定时间段内,数据一定是磁盘和内存中都有的。后续操作完内存数据之后,就需要以特定的刷新策略将数据刷新到磁盘中。此时,就涉及到磁盘和内存的数据交互,也就是IO了。而mysql中IO的基本单位就是page。

(5)为了能够更好的进行上面的操作,mysql服务器在内存中运行的时候,在服务器内部就申请了被称为“buffer pool”的大内存空间,这个空间默认为128MB,用于和磁盘数据进行IO交互。

(6)为了提高效率,一定要尽可能的减少系统和磁盘IO的次数。

上文中说mysql中有一个buffer pool内存空间,我们可以输入“vim /etc/my.cnf”打开数据库配置文件查看:

可以看到,配置文件中有这么一条注释,将这条指令放开,我们就可以修改buffer pool的大小。不放开的话大小默认为128MB。

你可能感兴趣的:(mysql数据库,数据库,mysql,sql)