MySQL索引

内容概要

  • 理解索引是什么,为什么需要索引
  • 掌握索引的数据结构,其他结构为什么不行
  • MySQL中索引的实现,MyISAM和InnoDB的主键索引和非主键索引
  • 理解什么是回表,什么是覆盖索引

1 索引

索引(Index)是数据库中一种特殊的数据结构,它用于提高数据库查询的效率和速度。在数据库中,索引类似于书籍中的目录,可以根据关键字快速定位到数据所在的位置,从而加速查询操作。

索引通常包括一个或多个列,每个列包含一个唯一的值,用于标识数据行。当查询语句包含一个或多个索引列时,数据库可以使用索引来快速定位符合条件的数据行,而不必扫描整个数据表。这可以大大提高查询速度,特别是对于大型数据表和复杂查询语句的情况下。

在数据库中,常用的索引类型包括主键索引、唯一索引、普通索引等。不同的索引类型适用于不同的查询场景,开发人员需要根据实际需求选择合适的索引类型。

需要注意的是,索引虽然可以提高查询效率,但也会占用一定的存储空间。因此,在设计数据库时需要仔细考虑索引的使用,避免过度使用索引导致数据库性能下降。同时,索引的维护也需要一定的时间和资源,因此需要根据实际情况定期进行索引优化和维护。

1.1 介绍

什么是索引呢?索引其实就是一种可以帮助我们提高查询速度数据结构

索引类似于一部字典开头的目录,可以帮助MySQL提高查询语句的效率。

1.2 索引的数据结构

索引是一个可以帮助我们高效获取数据的数据结构,那么索引采用的是什么样的数据结构呢?

去探讨一个数据结构适不适合当索引主要有以下三个考察指标:

  • 查询单个值

    SQL 复制代码
    select * from user where id=10;
    
    select * from user where name="zhangsan";
  • 查询范围值

    sql 复制代码
    select * from user where id between 1 and 10;
    select * from user where age < 18;
    
    -- 索引是不是只能是int的值
  • 插入数据

    sql 复制代码
    insert into user(name,age) values ("zhangsan", 20);

常见的数据结构

数组,链表,有序数组,二叉搜索树,B树,B+树,Hash表

数组:

  • 查找单个值,速度慢。因为要比较所有的数据

  • 查找范围值,速度慢,因为要比较所有数据

  • 插入值,速度快。

链表:

  • 查找单个值,速度慢。因为要比较所有的数据

  • 查找范围值,速度慢,因为要比较所有数据

  • 插入值,速度快。

有序数组

  • 查询单个值:速度快。采用二分法
  • 查询范围值:速度快。因为是有序的,先查找一个边界,然后再顺着走。
  • 插入数据:速度慢。因为插入一条数据,需要挪动数据。

有序数组,不适合用来做普通的索引,适合数据不要频繁插入和删除场景可以用它来做索引,例如:历史数据,2016年的淘宝订单。

二叉搜索树:

左小右大:左子树的所有值,小于根节点的值。右子树的所有值,大于根节点。

定义:

  • 它或者是一棵空树,或者是具有下列性质的二叉树:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

  • 它的左、右子树也分别为二叉排序树。

优点:

查找单个数据方便,查找范围值不方便。

缺点:

只有两个孩子。当数据量增大的时候,树的高度会升高,这个时候查询的次数就会变多。随着数据量的增大,会影响查询速度。

  • 查找单个值,速度快。

  • 查找范围值,速度中等,因为要在父节点和子节点之间反复跳转。

  • 插入数据,速度快。

特殊的二叉搜索树:红黑树

  • 查找单个值,速度快。
  • 查找范围值,速度慢,因为要在父节点和子节点之间反复跳转。
  • 插入数据,速度快。

B树

  • 查询单个值 比数组和链表要方便很多,比二叉树高度降低了,查询的效率也变高了
  • 查询范围值 查询范围值需要在父子节点之前反复查找,其实不太方便
  • 插入数据:速度快

B树对比红黑树和二叉树最大的进步:树的高度降低了,查询效率变高了。
为什么说树的高度降低了之后,查询效率会变高:

这个主要是和磁盘的读取策略以及 数据库的设计策略有关系。

结论:数据库在读取数据的时候,每一层会经过一次磁盘IO。假如数据的高度比较高,那么就需要经过多次的磁盘IO才能找到对应的数据,树的高度降低了之后,磁盘IO的次数会减少,那么这个时候查询速度增高。

B+树(Btree)

  • B+树其实就是在B树的基础之上进行了优化。
    • 叶子节点之间维护一个指针,方便了范围查找
    • 所有的非叶子节点,都在叶子节点中冗余一份
    • 所以的非叶子节点,只存储key,不存储data,会降低树的高度,进一步提高查询的效率。
  • 查询单个值 比较方便,速度快
  • 查询范围值 比较方便(因为叶子节点之间维护了一个指针,指向下一个叶子节点)
  • 插入值:方便
  • B+树其实也是MySQL官方推荐我们使用的数据结构。Btree
  • MySQL对标准的B+树做了一些优化。主要就是增加了回去的指针。

Hash表

在MySQL中,也有一种索引类型,叫做Hash索引,底层使用的是Hash表。

  • 查询单个值 很方便,对比B+树来说要方便一些
  • 查询范围值 很不方便,需要一个一个查。
  • 插入值: 方便

Hash索引是MySQL内部使用的一种索引,没有开放给用户使用。

我们选来做索引的就是B+树

问题1: 索引的结构为什么选B+树。为啥不选红黑树做索引。

问题2: 为什么索引结构是B+树?

先分析一些显而易见的不适合用来做索引的(数组 链表 有序数组)。再对比分析,为什么红黑树和二叉搜索树不行,主要的问题是存储信息的密度太低。

1.3 索引的实现

索引的实现其实就是去介绍一下,数据库中数据到底是怎样存储的。在介绍这个之前,我们需要先了解一下数据库的组成结构。

了解了MySQL的结构之后,那我们就可以知道,数据的存储和存储引擎息息相关。不同的存储引擎存储数据的方式是不一样的。

MySQL底层的存储引擎是作为一个插件存在。

存储引擎就是MySQL底层怎样组织这些数据。(这些数据最终都是在磁盘上的),也就是在磁盘上怎样组织这些数据。

在MySQL中,有很多种存储引擎

  • InnoDB(5.1之后默认的存储引擎),这个存储引擎其实一开始是以插件的形式存在的,在5.1之后,MySQL官方团队把InnoDB当成了默认的存储引擎。
  • MyISAM(5.1之前默认的存储引擎),这个存储引擎是由MySQL的官方团队开发的。
  • Memory(基本不用)

1.3.1 MyISAM

C:\ProgramData\MySQL\MySQL Server 5.7\Data

首先,来看一下MyISAM这种存储引擎是怎样存储数据的。

MyISAM的表都有三个文件:

  • .frm

    表结构定义文件。定义表结构, 表里面有哪些列,列的类型。

  • .MYD

    数据文件,其实也就是这个表中的数据都存储到这个文件中

  • .MYI

    索引文件,这个表中的所有的索引树都是存储在这个文件中

SQL 复制代码
mysql> create table t_myisam (
  ID int primary key,
  k int NOT NULL DEFAULT 0, 
  s varchar(16) NOT NULL DEFAULT '',
    -- index关键词 表明我想创建一个索引
    -- k 索引的名字
    -- (k) 索引列
  index k_name(k)
) engine=MyISAM;

insert into t_myisam values(100,1, 'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');

key就是主键。data存储的就是数据的地址(指向MYD文件中。)

MyISAM的索引分为两种类型,一种叫做主键索引,一种叫做非主键索引。

对于MyISAM来说,MYI文件里面存储的是索引,MYD文件里面存储的data。

对于它的主键索引,key是主键值,data是地址值。

对于它的非主键索引,key是索引值,data是地址。

1.3.1.1 主键索引

主键索引是指MyISAM默认会根据主键这一列的值,去建立一个B+树,这个B+树就叫做主键索引树。

  • key:主键值
  • data:主键这一行数据对应的地址值
1.3.1.2 非主键索引

MyISAM中的非主键索引,是指我们可以把其他的非主键列声明为索引列,那么这样MyISAM就可以帮助我们根据这一列的值去建立一个索引树。意味着一个表可以有多个索引树。

  • key:索引列的值
  • data:索引列这一行数据对应的地址值

对于MyISAM中的索引来说,数据和索引是分开存储的,这种索引叫做 非聚集索引。

1.3.2 InnoDB

InnoDB的索引分为两种类型,一种叫做主键索引,一种叫做非主键索引。

每一个InnoDB表都有两个文件:

  • .frm

    表结构定义文件

  • .ibd

    数据和索引文件:这个文件中存储了数据和索引。

SQL 复制代码
mysql> create table t_innodb (
  ID int primary key,
  k int NOT NULL DEFAULT 0, 
  s varchar(16) NOT NULL DEFAULT '',
  index k_name(k)
) engine=InnoDB;

insert into t_innodb values(100,1, 'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');
1.3.2.1 主键索引

key:主键值

data:主键这一行对应的其他列的数据

在InnoDB的主键索引中,索引和数据是存储在同一个数据页中,也就是索引和数据是存储在一起的,这种叫做聚集索引。

在InnoDB中,数据是依附于主键索引树来存储的,假如没有主键的话,那么就不存在主键索引树,那么数据也没办法存储。

所以对于InnoDB的表来说,必须得有一个主键。

对于InnoDB的表来说,如果用户在建表的时候,没有设置主键,那么InnoDB会维护一个隐藏的列来当做主键。

1.MyISAM使用索引存储。主键索引和非主键索引,其实存储的都是地址,都需要去数据文件中找这个数据

2.InnoDB使用索引存储,主键索引,直接存储的是数据。非主键索引,存储的是主键的值。

1.3.2.2 非主键索引

非主键索引是指根据其他的列建立的索引。

key:索引列的值

data:这一行数据对应的主键值

在InnoDB的非主键索引中,索引只和主键存储在到了一起,实际上没有和数据存储在一起,其实也是非聚集索引。

对于MyISAM来说,主键索引和非主键索引,都是怎么存的?

都是存的B+树,然后key是索引的值,data都是存的地址。这个地址是指向MYD文件里面的。

**对于InnoDB来说,**主键索引和非主键索引。

主键索引,存的B+树,key是主键的值,value是这一行的其他值。

(100,1,'aa')

对于非主键索引,key存的是索引的值,value是主键的值。

1.3.3 MyISAM 与InnoDB的区别

  • 存储的文件不一样,MyISAM有三个文件(frm、MYD、MYI)、InnoDB只有两个文件(frm 、ibd)。

  • InnoDB支持事务、MyISAM不支持事务

    既然MyISAM不支持事务,那么MyISAM还有没有用呢?

    什么样的表不需要事务呢?存储什么样的数据才不需要事务呢?什么样的数据不需要使用增删改呢? 历史数据。普通的日志数据。

  • InnoDB支持外键,MyISAM不支持外键

  • InnoDB支持表锁和行锁,MyISAM只支持表锁

1.4 Innodb举例

还是使用之前的数据。

SQL 复制代码
mysql> create table T (
  ID int primary key,
  k int NOT NULL DEFAULT 0, 
  s varchar(16) NOT NULL DEFAULT '',
  index k(k)
) engine=Innodb;

insert into T values(100,1, 'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');

如果现在查询一条SQL,经历怎样的过程?

如果查询的SQL是这样的: select * from T where id=500;

1.4.1 回表

select * from T where k =3;

  • 在k索引树上找到k=3的记录,取得ID=300
  • 再到ID索引树查到ID=300对应的行
  • 再到主键索引树上找到k=5的记录,发现不符合条件。

在这个过程中,回到主键索引树搜索的过程,我们称为回表。可以看到,这个查询过程读了 k 索引树的 2 条记录(步骤 1、3 ),回表了一次(步骤 2 )。在这个例子中,由于查询结果所需要的数据只在主键索引上有,所以不得不回表。那么,有没有可能经过索引优化,避免回表过程呢?

后续,需要什么字段,拿什么字段,不要直接写*。有可能会造成不必要的回表。

这个SQL,会经历怎样的过程?

select * from T where k between 3 and 5;

  • 去k索引树上找,k=3。拿到id=300
  • 回主键索引树,取得这一行的数据。
  • 去k索引树,往后拿5,拿到id=500
  • 回主键索引树,取得这一行的数据。 拿id=500的数据
  • 再往后拿,拿到k=6. 不符合条件,结束。

这个过程,我们在k索引树上读了三条记录 (3 5 6)。回表了两次(300 500)

1.4.2 覆盖索引

select ID from T where k =3

如果执行的语句是 这时只需要查 ID 的值,而 ID 的值已经在 k 索引树上了,因此可以直接提供查询结果,不需要回表。也就是说,在这个查询里面,索引 k 已经"覆盖了"我们的查询需求,我们称为覆盖索引。由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

今后建议大家,不要写 select * from T where k=3; 需要哪些列,你就把这些列,全部写出来。

1.5 索引的语法

我们给一个列声明为主键,默认主键这一列就会是主键索引(主键这一列会默认创建一个主键索引树)

sql 复制代码
-- 查询索引
show index from innodb_user;

drop table if exists student1;
-- 建立索引
create table student1(
	id int PRIMARY KEY,
	name varchar(20),
	age int(10),
	gender varchar(10),
    -- index  索引的名字(列名)
    -- index 索引的名字(列名1, 列名2)  联合索引
	index idx_name(name) using BTREE
)ENGINE=InnoDB ;

select * from student1;

show index from student;

-- 删除索引
-- alter table TABLE_NAME drop index INDEX_NAME;
alter table student1 drop index idx_name;

-- 添加索引
alter table student add index idx_age(age);

alter table student add index idx_name_age(name,age);

有时候发现,有一条SQL,特别慢,怎么办?

首先,需要看一下这个SQL,SQL是否写得有问题: select * from t where s = 'ee';

其次,如果条件没有办法动,尝试建索引。

explain语句 可以看查询的过程,查询中,是否走了索引。

select * from t where k=5 and s = 'ee';

2 面试题

  1. 索引采用的是什么数据结构?为什么采用这种数据结构

    B+树。列举一下其他的数据结构,对比一下。

  2. 数据库为什么推荐使用自定义主键,并且在MySQL中使用推荐使用主键自增的策略

    • 自定义主键:MySQL默认的使用的是InnoDB存储引擎,那么InnoDB存储引擎的数据和主键索引树是绑定在一起的,假如没有主键索引树,那么数据没有办法存储。假如没有给表指定主键的话,那么InnoDB会创建一个隐藏的列来当做主键,并建立主键索引树。假如使用了隐藏的列来当做的主键的话,那么我么查询的时候,就会浪费主键索引索引树带来的索引性能,所以推荐自己定义主键。

    • 自增的策略:

      因为自增的策略,在插入的时候,永远只会插入到索引树的右侧,那么这样就能保证树的结构不会发生比较大的改变,而结构改变是需要消耗时间的,所以这样就能保证插入的效率会比较稳定。

      SQL 复制代码
      create table student1(
      	id int primary key auto_increment,
          name varchar(255),
          
          create_time timestamp default current_timestamp,
          update_time timestamp default current_timestamp on update current_timestamp
      )
  3. InnoDB和MyISAM有什么区别?什么情况下使用MyISAM

  • 存储的文件不一样,MyISAM有三个文件(frm、MYD、MYI)、InnoDB只有两个文件(frm 、ibd)。

  • InnoDB支持事务、MyISAM不支持事务

    既然MyISAM不支持事务,那么MyISAM还有没有用呢?

    什么样的表不需要事务呢?存储什么样的数据才不需要事务呢?什么样的数据不需要使用增删改呢? 历史数据。普通的日志数据。

  • InnoDB支持外键,MyISAM不支持外键

  • InnoDB支持表锁和行锁,MyISAM只支持表锁

  1. 什么是回表?如何避免回表

    sql 复制代码
    create table ts(
    	id int PRIMARY KEY,
    	name varchar(20),
    	age int,
    	index idx_name(name,age) using BTREE
    )character set utf8;
    
    
    select * from ts where id = 1;  -- 查询主键索引树、查询速度快
    
    select * from ts where name='zhangsan';  --  会先去 idx_name索引树上找。 回表。
    
    select * from ts where age = 20;  -- 遍历整个主键索引树、查询速度很慢
    
    -- 先查询index_name 整个索引的索引树,查询到的结果是主键
    -- 再根据查询到的主键值 去主键索引树查询 对应的数据
    select id,name,age from ts where name = '张三'; 
    
    
    -- 最左匹配元素
    -- 索引的生效范围是最左开始的。   (name,age,address )
    -- 查询中,写什么,能走到索引?
    -- select * from ts where name='zhangsan';  -- 光走age不行。
    -- select * from ts where name='zhangsan' and age=20;
    
    -- (a, b, c, d)   用哪些作为查询条件,可以走这颗索引树   a       a,b     a,b,c    a,b,c,d

回表:在一次查询中,假如需要先根据非主键索引树查询主键值,然后再根据主键值查询主键索引树,这种查询了两遍索引树的情况,叫做回表。

在实际的工作中,应该要尽量避免回表的情况出现,如何避免呢?

  • 尽量使用主键查询

  • 尽量避免写 select *

  • 可以考虑使用联合索引.多个列创建一个索引。

  1. 索引性能这么好,是不是一个表建立的索引越多越好

    不是。

    1. 声明一个索引列,需要建立一个索引树,需要占用空间
    2. 假如声明的索引变多了之后,对应的索引树也会变多,查询的效率固然会提升,但是增删改的时候要去改变数据,改变数据势必会改变索引树的结构,维护这些索引树的成本也就提升了,增删改的效率也就降低了。

    那么一般针对一个表,建立几个索引比较合适呢?通常默认为一个表建立的索引不要超过5个。

  2. 什么样的列适合当索引

    • 数据不重复出现的

    • 值尽量不为空的(null)

    • 业务场景中查询条件比较多的

    • 这一列的值不经常变化的

相关推荐
TDengine (老段)19 分钟前
TDengine 字符串函数 CHAR 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
qq74223498430 分钟前
Python操作数据库之pyodbc
开发语言·数据库·python
姚远Oracle ACE1 小时前
Oracle 如何计算 AWR 报告中的 Sessions 数量
数据库·oracle
Dxy12393102161 小时前
MySQL的SUBSTRING函数详解与应用
数据库·mysql
码力引擎1 小时前
【零基础学MySQL】第十二章:DCL详解
数据库·mysql·1024程序员节
杨云龙UP2 小时前
【MySQL迁移】MySQL数据库迁移实战(利用mysqldump从Windows 5.7迁至Linux 8.0)
linux·运维·数据库·mysql·mssql
l1t2 小时前
利用DeepSeek辅助修改luadbi-duckdb读取DuckDB decimal数据类型
c语言·数据库·单元测试·lua·duckdb
安当加密2 小时前
Nacos配置安全治理:把数据库密码从YAML里请出去
数据库·安全
ColderYY2 小时前
Python连接MySQL数据库
数据库·python·mysql
IT教程资源C3 小时前
(N_084)基于jsp,ssm学生信息管理系统
mysql·jsp·ssm学生信息