谈谈MySql中的索引及其失效的情况

索引的定义与作用

索引是数据库管理系统中用于提高查询速度的一种数据结构。在MySQL 中,索引可以看作是一种特殊的表,其中包含了对数据表中特定列的值 及其在数据表中的位置信息

它就像是一本书的目录或字典,允许我们在很短的时间内找到所需的信息。 在数据库环境中,索引可以大大加快数据检索速度,减少处理时间和磁盘I/O次数。此外,它还可以确保对数据库记录的操作(如INSERT、UPDATE和DELETE)的完整性。因此,在设计数据库应用程序时,应充分考虑并合理利用索引技术。

索引的创建

建表时创建

sql 复制代码
 create table users(
     id        int unsigned auto_increment primary key,
     username  varchar(50)  not null,
     password  varchar(11)  not null
 )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

在创建表时,在表中定义一个primary key约束时,MySQL会自动为我们创建一个相应的主键索引。这也是所谓的隐式创建,可以通过如下语句来进行查看。

sql 复制代码
 show index from users;

建表后添加

当然,除了在创建表的时候定义,你也可以在已经定义好的表中进行添加。

sql 复制代码
 create index username_index on users(username);

以上语句就为users表又添加了一个普通索引(单列索引)

或者通过alter语句进行索引的创建。

sql 复制代码
 alter table users add index indexName(password);
 # ALTER TABLE `tableName` ADD PRIMARY KEY ( `column` ) 
 # 添加主键索引
 # ALTER TABLE `tableName` ADD UNIQUE (`column`)
 # 添加唯一索引
 # ALTER TABLE `tableName` ADD INDEX index_name (`column1`,`column2`)
 # 添加组合索引

以上就又创建了一个名为indexName的普通索引,同时列举了主键索引跟唯一索引的创建方式。

在创建索引方面,ALTER TABLE 和 CREATE INDEX 都可以达到相同的效果,但它们之间存在一些微妙的区别。例如,ALTER TABLE 可以同时执行多个更改,而 CREATE INDEX 则只能用于创建单个索引。

删除索引

在优化表时,如果遇到需要删除索引时,MySql也是支持的,可通过如下语句实现:

sql 复制代码
 # 删除索引
 # DROP INDEX index_name ON table_name;
 DROP INDEX username_index ON users;

这里我们就将刚刚创建的单列索引给删除了,只剩下最初建表时创建的主键索引。

被动创建

那如果我们在创建表的时候不设置索引,在后续也不给表添加索引,那该表是否就没有索引呢,它的查询效率是否就会很低?

MySql 8.0.30开始,MySQL支持为没有显式主键的InnoDB表生成不可见的主键。当sql_generate_invisible_primary_key服务器系统变量设置为ON时,MySQL服务器会自动将生成的不可见主键(GIPK)添加到任何这样的表中。

  • 查询对应设置
sql 复制代码
 SELECT @@sql_generate_invisible_primary_key;

该属性的默认值为:0。不开启,也就是说默认的情况下,MySql不会为没有创建的表创建主键(索引)。

sql 复制代码
 create table demos(
     username  varchar(50)  not null,
     password  varchar(11)  not null
 )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 # show index from demos;

未将sql_generate_invisible_primary_key的值设为on的情况下,是不会为其添加主键的。

接下来我们将其设置为on,再重新创建表看看。

sql 复制代码
 SET sql_generate_invisible_primary_key=ON;

| @@sql_generate_invisible_primary_key | ------------------------------------ 0 |

sql 复制代码
 create table demos(
     username  varchar(50)  not null,
     password  varchar(11)  not null
 )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 # show index from demos;

此时,就可以看到该表在创建成功后,自动有了一个名为PRIMARY的主键

相关链接:

MySql-官网文档

通过索引定位数据

MySQL常用的InnoDB引擎中,还是使用 B+树索引比较多。以下是MySql使用B+树进行数据存储的一个简略结构,

真实场景中最底层中每个节点(叶子节点)都有若干条数据,并且用其中的最小值作为一个标识符 ,而中间的节点则是存储的最底层中每个节点的最大值 (当然其中的数据也有若干条),并用其中最大值作为标识符 。而最上层的节点就是根节点,它存储的就是第二层中的子节点的标识符跟其他一些信息。

查找过程

比如我们去找43这个数,

  • 首先在跟节点进行比较,发现应该去最大的那个子节点图中为0033去查找
  • 接着在0033节点中跟内部元素进行比较,发现需要去最大的叶子节点中查找
  • 最后在叶子节点0033-0042中查找,经过一番比较过,没有找到,于是返回结果:没有该元素

以上就是如何通过主键索引查询数据的过程,每个数据节点存储的数据具体有些哪些这里就不展开了。

提示:使用InnoDB引擎创建主键索引,叶子节点存储了我们完整的用户记录

聚簇索引跟非聚簇索引

使用InnoDB 引擎创建的主键索引也叫聚簇索引(Clustered Index)。聚簇索引是指:将整个表的数据都存储在一个 B+树结构中的索引。该索引组织了表中的数据,使它们按照主键值排序并存储在一个 B+树中。聚簇索引的特点 :是可以快速访问指定范围内的记录,因为不需要多次查找就可以检索到所需要的数据。此外还有聚簇索引的叶子节点存储的是表中的数据

此外还有非聚簇索引,在MySql中,非聚簇索引是指在 InnoDB 引擎中除了主键索引以外的所有其他索引类型,如:唯一索引,组合索引...非聚簇索引不同于聚簇索引,它不包含表的实际数据,而是指向数据的指针。当需要从非聚簇索引中检索数据时,MySQL 需要执行一次回表操作,以便访问聚簇索引并将数据返回给客户端。非聚簇索引也叫二级索引,或者叫辅助索引。它叶子结点存储的则是主键值索引列

使用非聚簇索引查找数据的过程一般情况如下:

  1. 首先,MySQL 会在非聚簇索引的 B+ 树上找到匹配的索引项,并读取指向实际数据行的指针;
  2. 然后,MySQL 会根据这些指针从聚簇索引中检索出相关行,然后将其返回给客户端。

从非聚簇索引转到聚簇索引的这个过程就叫做回表

注意:通过非聚簇索引查询不是一定会发生回表,如果查询完全符合非聚簇索引的条件,则可以直接从非聚簇索引中检索出所需的记录。在这种情况下,无需进一步回表即可完成查询。

索引的一些类型

  • 主键索引:设定为主键后,数据库自动建立索引,InnoDB为聚簇索引,主键索引列值不能为空(Null)
  • 唯一索引:索引列的值必须唯一,但允许有空值(Null),但只允许有一个空值(Null)
  • 复合索引:一个索引可以包含多个列,多个列共同构成一个复合索引
  • 全文索引:全文索引是一种特殊类型的索引,用于在文本数据上进行高效的全文搜索。
  • 空间索引: MySQL 提供的一种用于索引地理位置数据的技术,它使用 R 树结构来提高查询性能。

索引失效的情况

查看一个语句是否走索引,可以通过如下sql进行查看

sql 复制代码
 #explain select * from tableName where 条件
 explain select * from users where password="222" 

若对应的key有值,则为查询时走了索引。

不满足最左匹配原则

使用联合索引时,比如我们建立了name, age, phone 三个字段的联合索引。

sql 复制代码
 alter table users add index uni_index(name,age,phone);
 # 不走索引
 explain select * from users where age=12 and phone= 1298672

因为最左优先原则:会先比较name属性,再接着age,最后phone,如没有,则无法匹配

所以如果where age = 12,则不会生效,而where age = 12 and name="test"则会生效。或者说只有(a),(a,b),(a,b,c)三种查询可以生效。

使用不等于(!= 、<>)

sql 复制代码
 explain select * from user where age != 20;

通常情况来说,使用不等于是不走索引的,但也有另外,MySQL中有一个叫做优化器的东西,他会对每一条查询sql做成本分析,然后根据分析结果选择是否使用索引或者全表扫描。

这里举2种情况,如果不等于20的记录跟等于20的记录,数量都比较多,则成本很高,不会走索引。但如果本身记录很少,比如一共只有几条,则会走索引,或者不等于2的仅有10条,也会走索引。

总结:使用不等于的情况下,不一定不走索引,而是看成本,如果走索引成本不大,也会走索引,否则不会。

"OR"前后存在非索引列,导致索引失效

当where语句中使用or操作符并且两边的条件至少涉及两个字段,且字段中存在非索引列时,MySQL无法使用索引,会转向全表扫描

index_merge: OR前后的两个条件中的列都是索引时,查询中才使用索引,索引类型是"index_merge",将两个索引字段分别扫描,然后合并。

sql 复制代码
 explain select * from diy.users where age =88 or hobby ='看电影';
 # 使用了索引
 explain select * from diy.users where age =88 or hobby ='看电影' or password='123'
 # 以上语句 则没有使用索引

在上述两个语句中,因为age,hobby都使用了索引,所以第一个sql走了索引,而password没有索引,所以第二个sql不会走索引。

like中用通配符开头('%xxx')

sql 复制代码
 # 在一个表中创建一个索引
 alter table diy.users add index h_index(hobby);
 # 以下语句 使用了索引
 explain select * from diy.users where hobby like '看%'
 # 未使用索引 花费时间更久
 explain select * from diy.users where hobby like '%看'
  1. %通配符:表示任意字符,可以匹配任意的字符数量。
  2. _通配符:表示任意单个字符。

使用Order By

sql 复制代码
 explain select * from diy.users order by id,age;

以上sql语句,order后跟了2个字段,其中id是主键,但age没建立索引,则在查询时不会走索引。这是因为:ORDER BY子句中的列与索引列不完全匹配(即索引列包含部分数据而非完整列),所以MySQL可能无法有效地利用索引进行排序。

通常来说,order by后跟的列只要是索引列就会走索引,但有时即使order by后跟的列是索引列,也会发生索引失效的情况,这是因为优化器的作用,可能是由于查询优化器的估算成本较高,或者使用文件排序的开销较小等原因。

在索引列上进行计算、函数

现有一个需求,找出name为ty开头的字段信息:

sql 复制代码
 create index idx_name on demos(name);
 # 对索引列执行函数计算
 explain select * from demos where left(name,2)='ty'

字段使用函数会让MySql中的优化器无法生效,因为函数的结构可能和B+树中的值不匹配,所以不会走索引。字段能不用函数就不用函数。

Select *

sql 复制代码
 select * from tableName

使用select * 查询,不加任何条件,也不会走索引,直接返回全部结果,没有用到任何索引,查询效率很低

还有其它的一些索引失效的情况,这里只列举了一些常用的。

优缺点与建议

优点:它类似于书的目录,降低了数据库IO成本,大大提高了数据查询效率

缺点:索引也是数据,因此会占据一些磁盘空间,同时由于索引跟数据存在一定关联关系,在改动数据时,也需要同时对它进行更新,一定程度上减低了更新表的效率(每次对表进行增删改查,MySQL不仅要保存数据,还有保存或者更新对应的索引文件)。

总结:索引确实能提高查询效率,但并非每次查询都会奏效,因为它独特的数据结构,有时也会存在失效的情况。索引其实就是用空间换取时间的典型例子。

数据库表中不是索引越多越好,而是根据情况为那些常用的搜索字段建立索引,效果才是最佳!

一些扩展资料

菜鸟教程-MySql索引

数据结构可视化网站

相关推荐
姚远Oracle ACE8 小时前
解读Oracle AWR报告:Global Cache and Enqueue Services - Workload Characteristics
数据库·oracle
流星白龙8 小时前
【Qt】7.信号和槽_connect函数用法(2)
java·数据库·qt
Zzz 小生10 小时前
Claude Code学习笔记(四)-助你快速搭建首个Python项目
大数据·数据库·elasticsearch
nongcunqq13 小时前
abap 操作 excel
java·数据库·excel
rain bye bye14 小时前
calibre LVS 跑不起来 就将setup 的LVS Option connect下的 connect all nets by name 打开。
服务器·数据库·lvs
冻咸鱼14 小时前
MySQL的配置
mysql·配置
阿里云大数据AI技术15 小时前
云栖实录|MaxCompute全新升级:AI时代的原生数据仓库
大数据·数据库·云原生
不剪发的Tony老师16 小时前
Valentina Studio:一款跨平台的数据库管理工具
数据库·sql
weixin_3077791316 小时前
在 Microsoft Azure 上部署 ClickHouse 数据仓库:托管服务与自行部署的全面指南
开发语言·数据库·数据仓库·云计算·azure
六元七角八分16 小时前
pom.xml
xml·数据库