MySQL数据库操作篇5(索引的原理及用法)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: MySQL数据库操作篇5(索引的原理及用法)

索引是MySQL中的一大利器,这篇文章旨在讲清楚什么是索引,索引的底层实现原理是什么,使用索引能带来哪些好处,以及索引如何使用较好

此文章会用到前面提到过的一些术语,比如元组,一个元组就指表中的一行数据


什么是索引

通过前面的学习,我们了解过,在数据库中创建一个表,我们就可以往表中插入数据,然后需要数据的时候在表中查找就行了,普通用户可以直接使用数据库设计者提供的功能,不用关心如何实现查找,作为程序开发人员,我们需要关心这个问题。前面我们讲了很多MySQL中查找数据的方法,但是这些查找总得有个底层的算法实现吧,我们最容易想到的查找算法是遍历,把表中所有的数据都遍历一遍,不就可以查到想要的数据了嘛

对于小批量的数据,确实可以这么做,但是数据库就是存放大批量数据的,一张表中可能有几万行数据,如果这个时候还使用遍历查找,那么查找效率会非常的低,为了解决这个问题,数据库设计者就提出了索引这个概念,所以索引就是来解决数据查找效率的问题的

索引的实现是靠比较优秀的查找算法,不用加内存,不用改程序,不用调sql,只要执行

正确的 create index ,查询速度就可能提高成百上千倍。但是天下没有免费的午餐,查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的IO。所以它的价值,在于提高一个海量数据的检索速度


索引的底层实现原理

搞明白索引是干嘛的,有什么用之后,我们接着来看,如何实现索引。为了更透彻的理解索引,此篇文章要从硬件和软件两个方面同时介绍。

查找过程本质就是在做排除,把无用数据排除在外,高效率的查找就是在较短的时间内,排除掉尽可能多的数据,要做到这一点,就需要我们人为把数据分配整齐,想一想我们按照类别给数据进行划分,只需要去待查找数据的所属类别里查找即可,此时其他所有的类别就都被排除掉了。

例如我们书本中常见的目录就是一种高效的查询方式,书本根据内容的不同,划分为几个章节,目录中记录着这些章节的具体页码,当想要查找某个信息时,只需要去该信息所属的那个章节即可,而其他的章节在这个查找过程中就被排除掉了,这样就能很快查到信息,比遍历一本书要快速很多,索引的实现原理就类似于此

我们先来了解MySQL与磁盘的交互,关于磁盘的内容,笔者在Linux的IO篇里详细的讲解了,如果不明白磁盘工作原理的,请移步查看。操作系统每次与磁盘IO的大小是4kb。大家可以想想,每次4kb字节的IO,对于操作系统来说可能就够用了,但是对于数据库来说,小了一些,因为数据库中存放的数据是相当大的,因此数据库需要与磁盘一次IO更多,根据实践,数据库一次与磁盘进行16kb的IO是比较好的

但是之前我们了解过,只有操作系统才能够和硬件进行交互,而数据库本质是属于应用层的,所以它没有权利和磁盘直接进行IO,它与磁盘的IO操作要依托操作系统来完成

MySQL创建表本质就是创建一个文件,所以MySQL与操作系统进行交互时,也是通过文件来进行的,如上图,MySQL会申请一个缓冲区用来存取与OS进行IO操作的数据

这个过程是麻烦的,我们在逻辑上可以忽略OS这个角色,认为MySQL直接与磁盘进行IO操作,且一次16kb大小,也就是说MySQL 和磁盘进行数据交互的基本单位是 16KB 。这个基本数据单元,在 MySQL 这里叫做page(注意和系统的page区分)

为什么要提到page呢?因为MySQL 中的数据文件,是以page为单位保存在磁盘当中的,一张表,也就是一个数据文件,是由一个或者多个page构成的。MySQL中的数据文件以page为单位,势必会造成存储空间不能完全利用的情况,为什么要以page为单位,而不是存多少用多少呢?这就涉及到IO的效率了,我们都知道与磁盘的IO效率是很低的,如果一次以表中一个元组(表中的一行)的大小为单位,那么在查找时,有100个元组就要进行100次IO操作,而使用page为单位,一次IO就可以把100个元组的数据都加载进内存

各个page之间通过双向链表连接起来,每个page里包含着所存的数据内容,同时也有指向前一个和后一个page的指针

大家可以想一想,如果一个page里存满了数据,那么在一个page里查找数据,如果使用线性遍历的方法进行查找的话,效率会很低,还记得我们前面的提到过比较高效的目录形式吗?为了提高在一个page中遍历的效率,我们可以将page中的数据先按顺序进行一个排序,排完序之后再将其按照序号划分成目录,就拿上面这个page为例来进行划分,划分结果如下图

这里数据量比较小,不过不影响我们理解这种思想,假设查找id为4的人是谁,大致过程为在目录区中使用二分法查找id为4属于哪个目录,图中只有两个目录,第二个目录标记的最大id值为3,因为4>3,我们就从目录二进入到id为3的数据,然后向下移动一位,就找到了id为4的数据,随后返回结果即可。这里数据量很小,效果并不明显,随着数据量增大,这种查询算法的优势愈加明显

当然,我们前面说过一张表可能有一个page或者多个page构成,为了展示整张表的全貌,这里假设一个page只能存放五个元组,MySQL中一张表的存储结构大致如下(为了作图方便,数据中人物的名字是重复的,只关注数据的id序号即可)

理解完上面的内容,接下来还得思考一些问题,数据库就是用来存放数据的,那么大量的数据势必会造成page数目的剧增。我们前面解决的是如何在一个page内部高效的查找数据,当page数目变得非常多的时候,我们就得考虑,如何在page和page之间进行高效的查找,以上图为例,假设我们要查找id值为14的全部数据,那么会先加载第一个page,通过内部的查询后发现不在该page里,于是加载第二个page,通过内部的查询后发现不在该page里,于是加载第三个page,终于在内部查询到了,返回查询结果

将每一个page加载到内存,进行线性检测。这样就显得我们之前的page内部的目录,有点杯水车薪了,那么如何解决呢?解决方案,其实就是我们之前的思路,给page也带上目录

使用一个目录项来指向某一页,这个目录项存放的是将要指向的页中存放的最小数据的键值

和页内目录不同的地方在于,这种目录管理的级别是页,而页内目录管理的级别是行

其中,每个目录项的构成是:键值+指针

大致过程如下图

将page也放到一个目录中,这样就不用怕随着page数目的增多,查询效率低下的问题了

在上图中最下层,存放着真实数据的页,我们称之为普通页,而第二层,标志普通页的目录的页我们称之为目录页,目录页的本质也是页,普通页中存的数据是用户数据,而目录页中存的数据是普通页的地址

虽然目录页改善了普通页的查找效率,但我们每次检索数据的时候,该从哪里开始呢?虽然顶层的目录页少了,但是还要遍历啊?不用担心,我们还可以给目录页再加一层目录页

大致操作如下图

过这三层目录结构的搭配使用,查找数据的效率将会变得非常之高,这个结构其实就是B+树, 也是索引的底层实现原理,不过这并不意味着,所有的数据库的索引都是靠B+树来实现的,这取决于数据库使用的存储引擎,我们常用的InnoDB使用的就是B+树来实现,也有的存储引擎使用B树,使用哈希等结构,根据不同的应用场景来选择合适的


B树 VS B+树

B树和B+树都是常用的数据结构,也是MySQL数据库都支持的结构形式,不同的存储引擎所使用的数据结构都不尽相同,这里简单展示一下B树和B+树是如何在数据库中应用的,以及两者之间的区别,关于这两种数据结构如何创建,笔者将会在数据结构栏里详谈

下图是B树的结构图

下图是B+树的结构图


聚簇索引 vs 非聚簇索引

在使用索引时,我们可能经常会听到聚簇索引和非聚簇索引这些概念,这些具体指代的是什么呢?如下图,在这个B+树索引中,叶子节点存放的并不是数据,而是数据在内存上的指针,通过这个指针就可以找到数据,大家可以发现,在这个过程中,数据就和索引结构分离开了,像这样的索引方案,我们就称之为非聚簇索引

在下面这张图中,B+树的叶子节点存放的就是数据本身,而不是数据的指针,像这样索引结构和数据放在一起的我们就称之为聚簇索引

InnoDB是采用聚簇索引,MyISAM采用的是非聚簇索引,不过这两个存储引擎的索引结构均是B+树,你可能好奇,为什么要分成聚簇索引和非聚簇索引呢?下面就是两者各自的优缺点

聚簇索引的优点:

 聚簇索引将数据行物理上按照索引的顺序存储在磁盘上,因此对于按索引列进行范围查询或排序的操作,聚簇索引可以提供更高的查询性能

 聚簇索引可以减少磁盘I/O操作,因为相邻索引键值的数据行通常在磁盘上也是相邻存储的,减少了磁盘读取的次数

 聚簇索引可以节省存储空间,因为数据行按照索引顺序存储,可以减少数据行中重复的索引列的存储

聚簇索引的缺点:

 聚簇索引的维护代价相对较高。当插入或删除数据时,因为数据行的物理存储顺序可能需要进行调整,所以维护聚簇索引的代价较高

 聚簇索引一般只能创建在唯一键或主键上,对于非唯一键的查询性能可能较差

非聚簇索引的优点:

 非聚簇索引的维护成本相对较低。当插入或删除数据时,不需要调整数据行的物理存储顺序,因此维护非聚簇索引的代价较低

 非聚簇索引可以创建在任意列上,不限唯一键或主键。可以提供对非唯一键的高效查询

非聚簇索引的缺点:

 非聚簇索引的查询性能可能较低。因为数据行在磁盘上的物理存储顺序与索引顺序不一定一致,对于范围查询或排序操作,可能需要进行更多的磁盘I/O操作

 非聚簇索引通常需要占用更多的存储空间,因为索引结构中需要存储索引列的值和指向数据行的指针。 根据具体的应用场景和查询需求,选择合适的索引类型可以提高数据库的查询性能。聚簇索引适用于经常进行范围查询或排序的列,而非聚簇索引适用于非唯一键或不经常进行范围查询的列。


需要大家注意的是,聚簇索引一定要有唯一键索引,也就是主键索引,如果自己没有设置,MySQL会默认设置,除了默认会建立主键索引外,我们用户也有可能建立按照其他列信息建立的索引,一般这种索引可以叫做辅助(普通)索引,关键点来了,普通索引的叶子节点可不像主键索引那样存放着数据,而是存放着对应的主键值,拿到这个主键值后,还要回过头根据这个主键值,再去查一遍主键索引,最终找到数据,这个过程叫做回表查询,这也是为什么强调聚簇索引一定要有主键索引的原因。为什么不把普通索引的叶子节点也放上数据呢?因为主键所引上已经存放数据了,再存放一次就会造成数据的重复,空间的极大浪费


索引的相关操作

了解完索引是什么,以及索引的底层实现原理之后,我们接下来进入实操环节,看看如何在MySQL中创建和使用索引,在创建数据库时要根据未来的需求来确定采用哪种存储引擎,默认情况下都是采用InnoDB存储引擎

1.创建主键索引

在InnoDB中会默认根据主键创建主键索引,故而只需要指明主键即可

1.-- 在创建表的时候,直接在字段名后指定 primary key

create table test(id int primary key, name varchar(30));

在InnoDB中会默认根据主键创建一个主键索引

2.-- 在创建表的最后,指定某列或某几列为主键索引

create table test(id int, name varchar(30), primary key(id));

3.create table test(id int, name varchar(30));

-- 创建表以后再添加主键

alter table test add primary key(id);

主键索引的特点:

一个表中,最多有一个主键索引,当然可以使符合主键

主键索引的效率高(主键不可重复)

创建主键索引的列,它的值不能为null,且不能重复

主键索引列的数据类型基本上是int,有利于数据内容的排序和划分


2.创建唯一键索引

在InnoDB中会默认根据唯一键创建唯一键索引,故而只需要指明唯一键即可

1. -- 在表定义时,在某列后直接指定unique唯一属性。

create table test(id int primary key, name varchar(30) unique);

2.-- 创建表时,在表的后面指定某列或某几列为unique

create table test(id int primary key, name varchar(30), unique(name));

3.create table test(id int primary key, name varchar(30));

-- 创建表以后再添加唯一键

alter table test add unique(name);

唯一索引的特点:

一个表中,可以有多个唯一索引

查询效率高

如果在某一列建立唯一索引,必须保证这列不能有重复数据

如果一个唯一索引上指定not null,等价于主键索引


3.创建普通索引

1.create table test(id int primary key,

name varchar(20),

email varchar(30),

index(name) --在表的定义最后,指定某列为索引

);

2.create table test(id int primary key,

name varchar(20),

email varchar(30),

);

alter table user9 add index(name); --创建完表以后指定某列为普通索引

3.create table test(id int primary key,

name varchar(20),

email varchar(30),

);

-- 创建一个索引名为 idx_name 的索引

create index idx_name on test(name);

普通索引的特点:

一个表中可以有多个普通索引,普通索引在实际开发中用的比较多

如果某列需要创建索引,但是该列有重复的值,那么我们就应该使用普通索引


索引的查询

第一种方法: show keys from 表名
第二种方法: show index from 表名;
第三种方法(信息比较简略):desc 表名;


删除索引

第一种方法-删除主键索引: alter table 表名 drop primary key;

第二种方法-其他索引的删除: alter table 表名 drop index 索引名;

索引名就是show keysfrom 表名中的 Key_name 字段

第三种方法方法: drop index 索引名 on 表名


索引创建原则

比较频繁作为查询条件的字段应该创建索引
唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件
更新非常频繁的字段不适合作创建索引
不会出现在where子句中的字段不该创建索引

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
16天前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
117 9
|
17天前
|
存储 SQL 关系型数据库
MySQL进阶突击系列(03) MySQL架构原理solo九魂17环连问 | 给大厂面试官的一封信
本文介绍了MySQL架构原理、存储引擎和索引的相关知识点,涵盖查询和更新SQL的执行过程、MySQL各组件的作用、存储引擎的类型及特性、索引的建立和使用原则,以及二叉树、平衡二叉树和B树的区别。通过这些内容,帮助读者深入了解MySQL的工作机制,提高数据库管理和优化能力。
|
20天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化以及慢查询优化
通过本文的介绍,希望您能够深入理解MySQL索引优化和慢查询优化的方法,并在实际应用中灵活运用这些技术,提升数据库的整体性能。
60 18
|
13天前
|
存储 Oracle 关系型数据库
索引在手,查询无忧:MySQL索引简介
MySQL 是一款广泛使用的关系型数据库管理系统,在2024年5月的DB-Engines排名中得分1084,仅次于Oracle。本文介绍MySQL索引的工作原理和类型,包括B+Tree、Hash、Full-text索引,以及主键、唯一、普通索引等,帮助开发者优化查询性能。索引类似于图书馆的分类系统,能快速定位数据行,极大提高检索效率。
48 8
|
19天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化以及慢查询优化
通过本文的介绍,希望您能够深入理解MySQL索引优化和慢查询优化的方法,并在实际应用中灵活运用这些技术,提升数据库的整体性能。
22 7
|
18天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化与慢查询优化:原理与实践
通过本文的介绍,希望您能够深入理解MySQL索引优化与慢查询优化的原理和实践方法,并在实际项目中灵活运用这些技术,提升数据库的整体性能。
50 5
|
8天前
|
存储 关系型数据库 MySQL
【MYSQL】 ——索引(B树B+树)、设计栈
索引的特点,使用场景,操作,底层结构,B树B+树,MYSQL设计栈
|
10天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
38 3
|
10天前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
37 3
|
10天前
|
SQL 关系型数据库 MySQL
数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog
《数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog》介绍了如何利用MySQL的二进制日志(Binlog)恢复误删除的数据。主要内容包括: 1. **启用二进制日志**:在`my.cnf`中配置`log-bin`并重启MySQL服务。 2. **查看二进制日志文件**:使用`SHOW VARIABLES LIKE 'log_%';`和`SHOW MASTER STATUS;`命令获取当前日志文件及位置。 3. **创建数据备份**:确保在恢复前已有备份,以防意外。 4. **导出二进制日志为SQL语句**:使用`mysqlbinlog`
52 2